zoukankan      html  css  js  c++  java
  • Java中Set的contains()方法

    Java中Set的contains()方法 —— hashCode与equals方法的约定及重写原则

    翻译人员: 铁锚
    翻译时间: 2013年11月5日
    原文链接: Java hashCode() and equals() Contract for the contains(Object o) Method of Set

    本文主要讨论 集合Set 中存储对象的 hashCode 与 equals 方法应遵循的约束关系.

    新手对Set中contains()方法的疑惑
    import java.util.HashSet;
     
    class Dog{
    	String color;
     
    	public Dog(String s){
    		color = s;
    	}	
    }
     
    public class SetAndHashCode {
    	public static void main(String[] args) {
    		HashSet<Dog> dogSet = new HashSet<Dog>();
    		dogSet.add(new Dog("white"));
    		dogSet.add(new Dog("white"));
     
    		System.out.println("We have " + dogSet.size() + " white dogs!");
     
    		if(dogSet.contains(new Dog("white"))){
    			System.out.println("We have a white dog!");
    		}else{
    			System.out.println("No white dog!");
    		}	
    	}
    }

    上述代码的输出为:
    We have 2 white dogs!
    No white dog!

    程序中添加了两只白色的小狗到集合dogSet中. 且 size()方法显示有2只白色的小狗.但为什么用 contains()方法来判断时却提示没有白色的小狗呢?

    Set的contains(Object o) 方法详解
    Java的API文档指出: 当且仅当 本set包含一个元素 e,并且满足(o==null ? e==null : o.equals(e))条件时,contains()方法才返回true. 因此 contains()方法 必定使用equals方法来检查是否相等.
    需要注意的是: set 中是可以包含 null值的(常见的集合类都可以包含null值). 所以如果添加了null,然后判断是否包含null,将会返回true,代码如下所示:
    HashSet<Dog> a = new HashSet<Dog>();
    a.add(null);
    if(a.contains(null)){
    	System.out.println("true");
    }

    Java的根类Object定义了  public boolean equals(Object obj) 方法.因此所有的对象,包括数组(array,[]),都实现了此方法。
    在自定义类里,如果没有明确地重写(override)此方法,那么就会使用Object类的默认实现.即只有两个对象(引用)指向同一块内存地址(即同一个实际对象, x==y为true)时,才会返回true。
    如果把Dog类修改为如下代码,能实现我们的目标吗?
    class Dog{
    	String color;
     
    	public Dog(String s){
    		color = s;
    	}
     
    	//重写equals方法, 最佳实践就是如下这种判断顺序:
    	public boolean equals(Object obj) {
    		if (!(obj instanceof Dog))
    			return false;	
    		if (obj == this)
    			return true;
    		return this.color == ((Dog) obj).color;
    	}
     
    }
    英文答案是: no.

    问题的关键在于 Java中hashCode与equals方法的紧密联系. hashCode() 是Object类定义的另一个基础方法.

    equals()与hashCode()方法之间的设计实现原则为:
    如果两个对象相等(使用equals()方法),那么必须拥有相同的哈希码(使用hashCode()方法).
    即使两个对象有相同的哈希值(hash code),他们不一定相等.意思就是: 多个不同的对象,可以返回同一个hash值.

    hashCode()的默认实现是为不同的对象返回不同的整数.有一个设计原则是,hashCode对于同一个对象,不管内部怎么改变,应该都返回相同的整数值.
    在上面的例子中,因为未定义自己的hashCode()实现,因此默认实现对两个对象返回两个不同的整数,这种情况破坏了约定原则。

    解决办法
    class Dog{
    	String color;
     
    	public Dog(String s){
    		color = s;
    	}
     
    	//重写equals方法, 最佳实践就是如下这种判断顺序:
    	public boolean equals(Object obj) {
    		if (!(obj instanceof Dog))
    			return false;	
    		if (obj == this)
    			return true;
    		return this.color == ((Dog) obj).color;
    	}
     
    	public int hashCode(){
    		return color.length();//简单原则
    	}
    }


    但是上面的hashCode实现,要求Dog的color是不变的.否则会出现如下的这种困惑:
    import java.util.HashSet;
    import java.util.Set;
    
    
    public class TestContains {
    
    
    	public static final class Person{
    		private String name = "";
    		public Person(String n) {
    			setName(n);
    		}
    		public String getName() {
    			return name;
    		}
    		public void setName(String name) {
    			this.name = (name==null)? "" : name;
    		}
    		@Override
    		public int hashCode() {
    			// 请考虑是否值得这么做,因为此时name是会变的.
    			return name.length();
    			// 推荐让name不可改变
    		}
    		@Override
    		public boolean equals(Object obj) {
    			if(!(obj instanceof Person)){
    				return false;
    			}
    			if(obj == this){
    				return true;
    			}
    			return this.name.equals(((Person)obj).name);
    		}
    	};
    	
    	public static void main(String[] args) {
    		Set<Person> persons = new HashSet<Person>();
    		//
    		Person person = new Person("tiemao");
    		persons.add(person);
    		// 修改name, 则依赖hash的集合可能失去作用
    		person.setName("ren");
    		// 同一个对象,居然是false,原因是我们重写了hashCode,打破了hashCode不变的基本约定
    		boolean has = persons.contains(person);
    		int size = persons.size();
    		System.out.println("has="+has);	// has=false.
    		System.out.println("size="+size);// size=1
    	}
    }

    参考文章: 
    http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html

    相关阅读

    1. Java equals() and hashCode() Contract

    2. HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap

    3. Java: Find all callers of a method – get all methods that call a particular method

    4. 理解Java机制最受欢迎的8幅图

  • 相关阅读:
    Intellij IDEA 使用jrebel运行spring-boot并实现自动编译进行热部署
    eclipse常见问题解决方案
    Spring Boot 打包部署
    解决oracle数据库 ora-00054:resource busy and acquire with NOWAIT specified 错误
    java RSA加密解密实现(含分段加密)
    Spring bean作用域
    博客目录
    分析SQL执行效率(一)
    索引操作
    表的基本操作
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6467086.html
Copyright © 2011-2022 走看看