浅谈原理--hashCode方法

 

咱们时常会判断一个元素是否相等重复,能够用equals方法。html

每增长一个元素,咱们就能够经过equals方法判断集合中的每个元素是否重复,可是若是集合中有10000个元素了,咱们每添加一个元素的时候,就须要进行10000此的equals方法的调用,显示效率很是的低下了。java

 

因而基于这种问题,java集合的设计者采用了哈希表来实现。算法

哈希表也称为散列算法,是依据数据特定算法产生的结果直接指定到一块地址上,这个结果由hashCode方法产生。数据库

这样一来,当集合每添加一个新的元素的时候,就能够经过hashCode方法直接定位到该存放的物理位置上,而不须要大量的equals板的比较。性能

 

上面说到了hashCode方法,它是Object类中的一个被native修饰的方法,优化

那么也就是说,咱们每一个对象都会继承了这个方法,咱们也就能够重写它了spa

 

Object类的hashCode方法代码:设计

public native int hashCode();

hashCode的比较方式code

  好比下方是在用HashSet存值htm

  1. 计算出来的位置上,若是这个位置上没有元素,它就能够直接存储在这个位置上了,不用进行任何的比较
  2. 若是这个位置有元素了,就调用它的(这个对象)equals方法与新的元素进行比较,相同的话就不存了
  3. 若是equals方法比较后,不相同,也就是放生了hashKey相同,致使冲突的状况。那么就在这个hashKey的地方产生一个链表,将全部产生相同的hashKey的对象添加到这个链表上,串在一块儿(不多会出现)。这样一来实际上咱们调用equals方法的概率就大大下降了。

 

下面以简单的图来表示

 

 

 

 这里有A B C D四个对象,分别经过hashCode方法产生了3个值

注意A和B对象调用hashCode产生的值是相同的,即 A.hashCode = B.hashCode()= 0x001

发生了哈希冲突,这时候因为最早插入了A,在插入B的时候,咱们发现B要插入A的位置,而A已经插入,也就是这个位置已经有对象了。

这个时候就经过调用equals方法判断A和B是否相同,若是相同就不插入B,若是不一样则将B插入到A后面的位置。

因此对于equals方法和hashCode方法有以下的要求:

 

1、hashCode要求

  1. 在程序运行期间,只要对象(字段)变化不会影响到equals方法的决策结果,那么在这个期间,不管调用多少次hashCode,都必须返回相同的散列码的hashCode
  2. 经过equals调用返回true的2个对象的hashCode必定相同
  3. 经过equals返回false的2个对象的hashCode不须要不一样,也就是容许hashCode相同。

所以获得如下结论

  • 两个对象相等,其hashCode必定相同
  • 两个对象不相等,其hashCode可能相等
  • hashCode相等的两个对象,不必定相同
  • hashCode不相等的两个对象,必定不一样

 

可能会有人疑问,对于不能重复的集合,为何不直接经过 hashCode 对于每一个元素都产生惟一的值,若是重复就是相同的值,这样不就不须要调用 equals 方法来判断是否相同了吗?

  实际上对于元素不是不少的状况下,直接经过 hashCode 产生惟一的索引值,经过这个索引值能直接找到元素,并且还能判断是否相同。好比数据库存储的数据,ID 是有序排列的,咱们能经过 ID 直接找到某个元素,若是新插入的元素 ID 已经有了,那就表示是重复数据,这是很完美的办法。但现实是存储的元素很难有这样的 ID 关键字,也就很难这种实现 hashCode 的惟一算法,再者就算能实现,可是产生的 hashCode 码是很是大的,这会大的超过 Java 所能表示的范围(由于返回值是int类型,大小只能是232),很占内存空间,因此也是不予考虑的。

 

2、重写hashCode

咱们应该注意:

  • 不一样对象的hashCode码应该尽可能不一样,避免hash冲突,也就是算法得到元素要尽可能均匀。
  • hash值是一个int类型,在java中占用4个字节,也就是232 次方,要避免溢出

 

下面是String的hashCode实现

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

这里有个数字 31 ,为何选择31做为乘积因子,并且没有用一个常量来声明?主要缘由有两个:

  ①、31是一个不大不小的质数,是做为 hashCode 乘子的优选质数之一。

  ②、31能够被 JVM 优化,31 * i = (i << 5) - i。由于移位运算比乘法运行更快更省性能。

  具体解释能够参考这篇文章

 

 

 

ps:

  对于Map集合,咱们能够选择Java中的基本类型,还有引用类型String做为key,由于它们都按照规范重写了equals方法和hashCode方法。

可是若是咱们自定义对象做为key,那么必定要覆盖equals方法和hahshCode方法,要否则会有未知的suprise等着你。

 

 

 

原文出处:https://www.cnblogs.com/arebirth/p/schashcodemethod.html

相关文章
相关标签/搜索