探索equals()和hashCode()方法

探索equals()和hashCode()方法

  在根类Object中,实现了equals()和hashCode()这两个方法,默认:
  equals()是对两个对象的地址值进行的比较(即比较引用是否相同),用==实现。
  hashCode():计算出对象实例的哈希码。根类Object的hashCode()方法的计算依赖于对象实例的内存地址,即内存地址由哈希函数生成一个int值,故每一个Object对象的hashCode都是惟一的;固然,当对象所对应的类重写了hashCode()方法时,结果就大相径庭了。之因此有hashCode方法,是由于在批量的对象比较中,hashCode要比equals来得快,不少集合都用到了hashCode,好比Hashtable。html

  • 两个obj,若是equals()相等,hashCode()必定相等
  • 两个obj,若是hashCode()相等,equals()不必定相等(Hash散列值有冲突的状况,虽然几率很低)。

  在集合中,判断两个对象是否相等的规则是:
第一步,若是hashCode()相等,则查看第二步,不然不相等;
第二步,查看equals()是否相等,若是相等,则两obj相等,不然仍是不相等。java

为何选择hashCode方法?

  好比set集合存储数据的时候是怎样判断存进的数据是否已经存在。使用equals()方法呢,仍是hashCode()方法。假如用equals(),那么存储一个元素就要跟已存在的全部元素比较一遍,好比已存入100个元素,那么存101个元素的时候,就要调用equals方法100次。
  但若是用hashcode()方法的话,每存一个数据就调用一次hashCode()方法,获得一个hashCode值及存入的位置。若是该位置不存在数据那么就直接存入,不然调用一次equals()方法,不相同则存,相同不存。这样下来整个存储下来不须要调用几回equals方法,虽然多了一次hashCode方法,但相对于前面来说效率高了很多。程序员

为何要重写equals方法?

  由于Object的equals()方法默认是两个对象的引用的比较,意思就是指向同一内存则相等,不然不相等;若是你如今须要利用对象里面的值来判断是否相等,则重载equals()方法。记住:String,Double、Integer、Math这些类已经重写了equals()方法,比较的是对象的值。算法

改写equals时老是要改写hashCode

  若是不这样作到话,就会违反Object.hashCode的通用约定:相等的对象必须具备相等的散列码hashCode。根据一个类的equals方法,两个大相径庭的实例有可能在逻辑上是相等的,可是,根据Object类的hashCode方法,它们仅仅是两个对象,对象hashCode方法返回两个看起来是随机的整数,而不是根据第二个约定要求的那样,返回两个相等的整数。从而致使该类没法与全部基于散列值(hash)的集合类结合在一块儿正常运做,这样的集合类包括hashMap、HashSet和Hashtable。好比new一个对象,再new一个内容相等的对象,调用equals方法返回的true,但他们的hashCode值不一样,将两个对象存入HashSet中,hashCode值不一样,均可以存进去,这样set中包含两个相等的对象。由于是先检索hashCode值,相等的状况下才会去比较equals方法。数组

hashCode方法使用介绍

  Hash表数据结构常识:
1、哈希表基于数组。
2、缺点:基于数组的,数组建立后难以扩展。某些哈希表被基本填满时,性能降低得很是严重。
3、没有一种简便的方法能够以任何一种顺序遍历表中数据项。
4、若是不须要有序遍历数据,而且能够提早预测数据量的大小,那么哈希表在速度和易用性方面是无与伦比的。缓存

为何HashCode对于对象是如此的重要(前面已经举了set的例子):

  HashMap和Hashtable,虽然它们有很大的区别,如继承关系不一样,对value的约束条件(是否容许null)不一样,以及线程安全性等有着特定的区别,但从实现原理上来讲,它们是一致的。因此,咱们只以Hashtable来讲明:
  在java中,存取数据的性能,通常来讲固然是首推数组,可是在数据量稍大的容器选择中,Hashtable将有比数组性能更高的查询速度。具体缘由看下面的内容:
  Hashtable在存储数据时,通常先将该对象的HashCode和0x7FFFFFFF作与操做,由于一个对象的HashCode能够为负数,这样操做后能够保证它为一个正整数。而后以Hashtable的长度取模,获得该对象在Hashtable中的索引。安全

index = (o.hashCode() & 0x7FFFFFFF)%hs.length;

  这个对象就会直接放在Hashtable的index位置,对于写入,这和数组同样,把一个对象放在其中的第index位置,但若是是查询,通过一样的算法,Hashtable能够直接从第index取得这个对象,而数组却要作循环比较。因此对于数据量稍大时,Hashtable的查询比数组具备更高的性能。数据结构

  事实上一个设计比较好的Hashtable,通常来讲会比较平均地分布每一个元素,由于Hashtable的长度老是比实际元素的个数按必定比例进行自增(负载因子通常为0.75左右),这样大多数的索引位置只有一个对象,而不多的位置会有几个元素。可是,hash冲突很难彻底避免,能够看hash。通常Hashtable中的每一个位置存放的是一个链表,对于只有一个对象的位置,链表只有一个首节点(Entry),Entry的next为null,同时保存hashCode,key,value属性,若是有相同索引的对象进来则会进入链表的下一个节点。若是同一个索引中有多个对象,根据HashCode和key能够在该链表中找到一个和查询的key相匹配的对象(equals方法)。
  对于一个对象,若是具备不少属性,把全部属性都参与散列,显然是一种笨拙的设计。由于对象的HashCode()方法被自动调用的不少,若是太多的对象参与了散列,那么须要的时间将会增长不少。能够挑选具备区分度的属性计算hash值,或者设立缓存,只要当参与散列的对象改变时才从新计算,不然调用缓存的hashCode,这能够从很大程度上提升性能。
  默认的实现是将对象内存地址转化为整数做为HashCode,这固然能保证每一个对象具备不一样的HasCode,但java语言并不能让程序员获取对象内存地址。
  请记住:若是你想有效的使用HashMap,你就必须重写在其的hashCode()。函数

还有两条重写hashCode()的原则:性能

  • 没必要对每一个不一样的对象都产生一个惟一的hashCode,只要你的HashCode方法使get()可以获得put()放进去的内容就能够了。即“不为一原则”。
  • 生成hashCode的算法尽可能使hashCode的值分散一些, 不要不少hashCode都集中在一个范围内,这样有利于提升HashMap的性能。即“分散原则”。
相关文章
相关标签/搜索