总结 equals 和 hashCode

这篇文章主要是总结自: Java hashCode() 和 equals()的若干问题解答

概述

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。

hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode() ,则一个 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

  

该方法主要是为了支持如 HashSet 、 HashMap 等基于哈希表实现的集合

也就是说:hashCode() 在散列表中才有用,在其它情况下没用。在散列表中 hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。

讨论 equals() 和 hashCode() 的关系应该分两种情况进行:

不会创建“类对应的散列表”

第一种是不会在 HashSet, Hashtable, HashMap 等基于哈希表实现的数据结构中用到该类这种情况下 equals() 和 hashCode() 没有关系,如果要比较对象,只需使用 equals() 来进行比较,而并不需要用到 hashCode() 所以重写 equals() 时不需要重写 hashCode()

比如只是要比较两个 Person 对象的 name 是否相同,但没有要将其放到 HashSet 等集合,只需重写 equals() 即可,如:

(来自参考链接)

public boolean equals(Object obj){  
    if(obj == null){  
        return false;  
    }  

    //如果是同一个对象返回true,反之返回false  
    if(this == obj){  
        return true;  
    }  

    //判断是否类型相同  
    if(this.getClass() != obj.getClass()){  
        return false;  
    }  

    Person person = (Person)obj;  
    return name.equals(person.name);  
} 

  

会创建“类对应的散列表”

第二种情况则是会在 HashSet, Hashtable, HashMap 等基于哈希表实现的数据结构中用到该类

这时需要注意两者的关系:

  • 若是两个对象使用 equals() 比较返回 true,那么使用 hashCode() 应该返回相同的哈希值。否则会出现 “ 两个对象是相同的(equals 返回 true),但却可以放进同一个哈希表 ” 的情况

    hashCode() 的 javaDoc :If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

  • 但如果使用 equals() 比较返回 false ,并不要求 hashCode() 返回不同的值。因为在哈希表中,即使两个哈希值相同(两个 key 相同),它们的值不一定相同(value 不一定相同),这种情况即哈希碰撞。

    hashCode() 的 javaDoc :It is not required that if two objects are unequal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results

    一个要注意的点是:在 HashSet 、 HashMap 等基于哈希表的集合中,若是两个对象使用 equals() 比较返回 false,但 hashCode() 返回相同的值(即产生哈希碰撞时),还是会将两个对象都放进集合(这时会使用哈希碰撞解决方法,如拉链法,红黑树等)

  

所以若是要在哈希表实现的数据结构中使用到某个类,则需要重写这个类的 equals() 和 hashCode() ,并保证它们满足上述关系

示例:(来自参考链接)

定义 Person 类:

class Person {
    int age;
    String name;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String toString() {
        return name + " - " +age;
    }

    @Override
    public int hashCode(){
        // 两个对象的 name 的大写相同且年龄相同时,返回相同的哈希值
        int nameHash =  name.toUpperCase().hashCode();
        return nameHash ^ age;
    }

    @Override
    public boolean equals(Object obj){
        if(obj == null){
            return false;
        }

        //如果是同一个对象返回true,反之返回false
        if(this == obj){
            return true;
        }

        //判断是否类型相同
        if(this.getClass() != obj.getClass()){
            return false;
        }

        // name 相同(区分大小写)且年龄相同返回 true 
        Person person = (Person)obj;
        return name.equals(person.name) && age==person.age;
    }
}

测试:

public static void main(String[] args) {
    // 新建Person对象,
    Person p1 = new Person("eee", 100);
    Person p2 = new Person("eee", 100);
    Person p3 = new Person("aaa", 200);
    Person p4 = new Person("EEE", 100);

    // 新建HashSet对象
    HashSet set = new HashSet();
    set.add(p1);
    set.add(p2);
    set.add(p3);
    set.add(p4);

    // 比较p1 和 p2, 并打印它们的hashCode()
    System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", 
                      p1.equals(p2), p1.hashCode(), p2.hashCode());
    // 比较p1 和 p4, 并打印它们的hashCode()
    System.out.printf("p1.equals(p4) : %s; p1(%d) p4(%d)\n", 
                      p1.equals(p4), p1.hashCode(), p4.hashCode());
    // p1.equals(p2) : true; p1(68545) p2(68545)
	// p1.equals(p4) : false; p1(68545) p4(68545)
    
    // 打印set,可以看到 p1 p4 equals返回 false,但 hashcode 相同,所以都被放进集合
    System.out.printf("set:%s\n", set);
    // set:[eee - 100, EEE - 100, aaa - 200]
}

如果不重写 hashCode() ,那么 p1、p2 都可以被放进 set 。

  

参考

Java hashCode() 和 equals()的若干问题解答

hashCode() 与 equals()

posted @ 2021-03-16 11:29  bxxiao  阅读(121)  评论(0)    收藏  举报