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

时间:2023-01-09 09:45:54
转自:http://blog.csdn.net/renfufei/article/details/14163329
翻译人员: 铁锚
翻译时间: 2013年11月5日
本文主要讨论 集合Set 中存储对象的 hashCode 与 equals 方法应遵循的约束关系.

新手对Set中contains()方法的疑惑

[java] view
plain
 copy
 
  1. import java.util.HashSet;
  2. class Dog{
  3. String color;
  4. public Dog(String s){
  5. color = s;
  6. }
  7. }
  8. public class SetAndHashCode {
  9. public static void main(String[] args) {
  10. HashSet dogSet = new HashSet();
  11. dogSet.add(new Dog("white"));
  12. dogSet.add(new Dog("white"));
  13. System.out.println("We have " + dogSet.size() + " white dogs!");
  14. if(dogSet.contains(new Dog("white"))){
  15. System.out.println("We have a white dog!");
  16. }else{
  17. System.out.println("No white dog!");
  18. }
  19. }
  20. }

上述代码的输出为:

[plain] view
plain
 copy
 
  1. We have 2 white dogs!
  2. 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,代码如下所示:

[java] view
plain
 copy
 
  1. HashSet a = new HashSet();
  2. a.add(null);
  3. if(a.contains(null)){
  4. System.out.println("true");
  5. }

Java的根类Object定义了  public boolean equals(Object
obj) 方法.因此所有的对象,包括数组(array,[]),都实现了此方法。
在自定义类里,如果没有明确地重写(override)此方法,那么就会使用Object类的默认实现.即只有两个对象(引用)指向同一块内存地址(即同一个实际对象,
x==y为true)时,才会返回true。
如果把Dog类修改为如下代码,能实现我们的目标吗?

[java] view
plain
 copy
 
  1. class Dog{
  2. String color;
  3. public Dog(String s){
  4. color = s;
  5. }
  6. //重写equals方法, 最佳实践就是如下这种判断顺序:
  7. public boolean equals(Object obj) {
  8. if (!(obj instanceof Dog))
  9. return false;
  10. if (obj == this)
  11. return true;
  12. return this.color == ((Dog) obj).color;
  13. }
  14. }

英文答案是: no.

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

equals()与hashCode()方法之间的设计实现原则为:

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

hashCode()的默认实现是为不同的对象返回不同的整数.有一个设计原则是,hashCode对于同一个对象,不管内部怎么改变,应该都返回相同的整数值.

在上面的例子中,因为未定义自己的hashCode()实现,因此默认实现对两个对象返回两个不同的整数,这种情况破坏了约定原则。

解决办法

[java] view
plain
 copy
 
  1. class Dog{
  2. String color;
  3. public Dog(String s){
  4. color = s;
  5. }
  6. //重写equals方法, 最佳实践就是如下这种判断顺序:
  7. public boolean equals(Object obj) {
  8. if (!(obj instanceof Dog))
  9. return false;
  10. if (obj == this)
  11. return true;
  12. return this.color == ((Dog) obj).color;
  13. }
  14. public int hashCode(){
  15. return color.length();//简单原则
  16. }
  17. }