equals()方法和hashCode()方法详解
equals()
方法和hashCode()
方法详解
1. Object
类中equals()
方法源代码如下所示:
1 | /** |
由以上源代码知,
Object
类中的equals()
方法是直接使用==
运算符来判断两个对象相等的。
- 引用类型变量使用
==
时,比较的是引用类型变量指向的对象的内存地址- 基本类型使用
==
时,比较值
Objcect
类中的hashCode
源代码如下:
1 | /** |
上面的注释中有说明如下几点:
- 对象的
hashCode
值通常是根据对象的内存地址计算得来- 两个对象
equals()
结果为true
时,两个对象的hashCode
值一定相等,不同对象的hashCode
不等native
标识此方法不是java
语言实现
Object
类中的toString()
方法源代码如下:
1 | public String toString() { |
2. String
类中equals()
方法和hashCode()
方法
String
类中部分源代码如下所示:
1 |
|
从上面的源码中,我们不难发现String
类已经重写了equals()
方法和hashCode()
方法。
String
类重写的equals()
方法判断流程如下:
- 使用
==
来判断两个对象的内存地址是否相同,相同返回true;
- 如果两个对象的内存地址不同,程序继续往下走,判断另一个对象是否是
String
类型的;- 如果比较对象不是
String
类型,直接返回false
;- 如果是
String
类型的,进行类型强转;- 比较两个
String
的字符数组长度,如果长度不同,返回false
;- 利用
while
循环来逐位比较字符是否相等,直到循环结束,所有字符都相等,则返回true
,否则返回false
;
下面来看一下重写的hashCode()
方法。
- 首先
String
类中定义了一个int
类型的变量hash
用来缓存String
对象的hash
值;- 如果当前调用
hashCode()
方法的String
对象在常量池没有找到,并且该对象的length
长度大于0
,则继续往下走,否则返回0
;即String
类默认""
字符串的hashCode()
值为0
;- 遍历字符数组,获取每一个字符的
ASCII
码表对应的值 和之前的hash
值相加,这样就保证了相同的字符串的hashCode()
返回值相同,计算公式在注释里已经写出来了:**s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
**- 将计算出来的结果保存到
hash
变量中,并返回该值;
这里为什么要乘以31
呢?原因是为了性能,不仅仅指降低了计算速度,也降低了哈希冲突的概率。
哈希冲突:此处指不同的字符串生成了相同的
hashCode
值。
31
是一个奇素数。如果乘数是偶数,并且乘法溢出的话,信息就会丢失,因为与2相乘等价于移位运算(低位补0
)。使用素数的好处并不很明显,但是习惯上使用素数来计算散列结果。 31
有个很好的性能,即用移位和减法来代替乘法,可以得到更好的性能: 31 * i == (i << 5)- i
, 现代的 VM
可以自动完成这种优化。这个公式可以很简单的推导出来。 —- 《Effective Java
》
素数:质数又称素数,指在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 wshawk's blog!