java equals() 和 hashCode()

equals() 判断两对象是否相等,hashCode() 计算对象哈希码

都不是 final 方法,都可被重写

使用和重写时,注意

Object 的equals()

1
2
3
public boolean equals(Object obj) {
    return (this == obj);
}

Object 实现区分度最高,两对象不是同一个对象, 一定返回 false

equals() 应遵守约定

  1. 自反:x.equals(x) 必须 true

  2. 对称:x.equals(y) 与 y.equals(x) 要相等

  3. 传递:x.equals(y) true,y.equals(z) true,x.equals(z) 必须 true

  4. 一致:x 和 y 在 equals() 中使用的信息都没有改变,x.equals(y) 值要始终不变

  5. 非 null:x 不是 null,y 为 null,x.equals(y) 必须 false

Object 的 hashcode()

1
public native int hashCode();

将对象内存地址作哈希码返回,保证不同对象返回值不同

  1. 哈希表中起作用

  2. 如果对象在 equals() 中使用的信息都没有改变,那么 hashCode() 值始终不变

  3. 如果两个对象使用 equals() 方法判断为相等,则 hashCode() 方法也应该相等

  4. 如果两个对象使用 equals() 方法判断为不相等,则不要求 hashCode() 也必须不相等;但是开发人员应该认识到,不相等的对象产生不相同的 hashCode 可以提高哈希表的性能

为什么要 hashCode()

插入时通过哈希码直接映射到哈希表中的位置

  1. 该位置没有对象,直接插入该位置

  2. 该位置有对象,调 equals() 比较对象是否相等,相等则不需保存;不相等,加入到链表中(jdk8优化为达到链表阀值用红黑树提升性能)


解释了为什么 equals() 相等,则 hashCode() 必须相等

equals()相等,哈希表中只出现一次;如 hashCode()不相等,会散列到哈希表不同位置,哈希表中出现了不止一次

String hashCode()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private final char value[];
private int hash; // Default to 0
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;
 
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

1、 String 数据是 final 的

1
2
String s = "hello"; 
s = "world";

s = “world” 并不是字符串对象的值变为了 “world”,而是新建了一个 String 对象,s 引用指向了新对象

2、 String 类将 hashCode() 的结果缓存为 hash 字段的值,提高性能

3、 String 对象 equals() 相等的条件是二者同为 String 对象,长度相同,且字符串值完全相同;不要求二者是同一个对象

4、 String 的 hashCode() 计算公式为:s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]

数字 31

  1. 质数计算哈希码,它与其他数字相乘之后,计算结果唯一的概率更大,哈希冲突的概率更小

  2. 质数越大,哈希冲突的概率越小,但计算速度也越慢;31 是哈希冲突和性能的折中,经验值

  3. JVM 会自动对 31 进行优化:31 * i == (i « 5) - i