Object.hashCode()与Object.equals()

Posted PacosonSWJTU

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Object.hashCode()与Object.equals()相关的知识,希望对你有一定的参考价值。

【README】

本文旨在po出 hashCode , equals的api描述,以加深理解;

本文翻译自 jdk 文档;


【1】Object.hashCode()

1)介绍:返回对象的哈希码值。支持此方法是为了有利于哈希表,例如由 java.util.HashMap 提供的哈希表。

在 Java 应用程序执行期间,只要在同一个对象上多次调用它,hashCode 方法必须始终返回相同的整数,前提是对象上的 equals 比较中使用的信息没有被修改。该整数不需要从应用程序的一次执行到同一应用程序的另一次执行保持一致。

hashCode方法需要遵守的约定:如果根据 equals(Object) 方法两个对象相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果(*干货)

如果根据 equals(Object) 方法两个对象不相等,则不需要对这两个对象中的每一个调用 hashCode 方法必须产生不同的整数结果。但是,程序员应该意识到为不相等的对象生成不同的整数结果可能会提高哈希表的性能。

尽可能实用,类 Object 定义的 hashCode 方法确实为不同的对象返回不同的整数。 (这通常通过将对象的内部地址转换为整数来实现,但 Java™ 编程语言不需要这种实现技术。)

补充: 可以看下 equals(), System.identityHashCode()


【1.1】String.hashCode()

返回此字符串的哈希码。 String 对象的哈希码计算如下
        s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]       
使用 int 算术,其中 s[i] 是字符串的第 i 个字符,n 是字符串的长度,^ 表示求幂。 (空字符串的哈希值为零。)
返回:此对象的哈希码值。

private final char value[]; // 只能被一次赋值,不可二次修改

 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;
    }

字符串底层是字符数组存储的,以字符串 aaa为例,存储结构为 ['a', 'a', 'a'] ,字符a的 ASCII 为97,则hash值计算步骤如下:

i 取值 0, 1, 2;初始时, h=hash=0;

ival[i]h
09797
19731*97+97 => 3104
29731*3104+97=> 96321    

所以字符串 aaa的hash值等于  96321 。

我们令 vi = val[i],则以后如下规则:

h0 = v0;

h1=31*v0+v1;

h2=31* [31*v0 + v1] + v2;

...

通过归纳法,我们可以得出字符串的hash值的计算公式为:

hn = 31^n * v0 + 31^n-1 * v1 + ...... + 31 * vn-1 + 31^0 * vn;  其中,vn表示字符串的第n个字符,n从0开始;

所以 字符串 "aaa" 的hash值 h2 = 31^2 * 97 + 31 * 97 + 97 = 96321 ;


【2】Object.equals ()

1)介绍:指示其他某个对象是否“等于”这个对象。
2)equals 方法在非空对象引用上实现等价关系:

  • 它是自反的:对于任何非空引用值 x,x.equals(x) 应该返回 true。
  • 它是对称的:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
  • 它是可传递的:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true 并且 y.equals(z) 返回 true,那么 x.equals(z) 应该返回 true。
  • 它是一致的:对于任何非空引用值 x 和 y,x.equals(y) 的多次调用始终返回 true 或始终返回 false,前提是对象的 equals 比较中使用的信息没有被修改。

对于任何非空引用值 x,x.equals(null) 应返回 false

Object 类的 equals 方法实现了对象上最有区别的可能等价关系;也就是说,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象(x == y 的值为 true)时,此方法才返回 true

请注意每当重写此方法时,通常都需要重写 hashCode 方法,以维护 hashCode 方法的一般约定,即相等的对象必须具有相等的哈希码

补充:也可以看看: hashCode(), java.util.HashMap


【3】System.identityHashCode(Object x)

1)介绍:为给定对象返回与默认方法 hashCode() 返回的相同的哈希码,无论给定对象的类是否覆盖 hashCode()。 空引用的哈希码为零


【4】HashMap.hash(Object key) 

 static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

计算 key.hashCode() 并将散列的较高位(异或)传播到较低位。

由于该表使用二次幂掩码,因此仅在当前掩码之上位变化的散列集将始终发生冲突。 (众所周知的例子是在小表中保存连续整数的浮点键集。)

因此,我们应用了一种将较高位的影响向下传播的变换。 位扩展的速度、效用和质量之间存在权衡。 因为许多常见的哈希集已经合理分布(因此不会从传播中受益),并且因为我们使用树来处理 bin 中的大量冲突,所以我们只是以最便宜的方式对一些移位的位进行异或以减少系统损失, 以及合并最高位的影响,否则由于表边界而永远不会在索引计算中使用。

以上是关于Object.hashCode()与Object.equals()的主要内容,如果未能解决你的问题,请参考以下文章

Object.toString 或 Object.hashCode 是不是曾经给出对象的内存地址

java核心技术-Object hashCode

Java 分析:java.lang.Object.hashCode 占用一半的 CPU 时间但从未显式调用

findBugs分类介绍

java.lang.NullPointerException: Cannot invoke "Object.hashCode()" because "key"

HashMap的底层实现原理