JCF之equals()与hashCode()关系详解

来源:互联网 发布:网络视频录像机 编辑:程序博客网 时间:2024/06/04 19:46

上一篇分析了equals()==的区别,这一篇,我们将一窥equals()hashcode()的关系。

API对于quals()的定义在上篇中已经解释过,这里再现一下:

/* equals 比较非空对象引用 自反性,x.equals(x)返回true 对称性:x.equals(y)与y.equals(x)结果相同 传递性:如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)返回true 一致性:多次执行x.equals(y)的结果相同 对于任何非空引用x,x.equals(null)返回false; */  public boolean equals(Object obj) {      return (this == obj);  } 

OK!现在,接下来让我们来看一下JDk 1.5 documentation中关于hashCode()的描述:

Returns a hash code value for the object. This method is supported for the benefit of hashtables such as those provided by java.util.Hashtable.

The general contract of hashCode is:

· Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the

 same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution 

 of an application to another execution of the same application.

· If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same 

integer result.

· It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the 

two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects 

may improve the performance of hashtables.

As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically 

implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.) 

大意如下:

返回对象的hashCode值.为hash类容器的使用提供支持。

  hashCode基本规范:

  • 在一个Java应用程序执行期间,如果一个对象中与equals()相关的信息没有被修改,那么,每次调用hashCode()都会返回同一个int(hashCode)
  • 假设有两个对象x,y;如果x.equals(y)==true,x.hashCode()y.hashCode()得到的值必须相等
  • 假设有两个对象x,y;如果x.equals(y)==false,,x.hashCode()y.hashCode()得到的值可能相等,也可能不相等

这一基本规范也可以概括为一句话:

如果两个对象相等(x.equals()==true),则它们的hashCode相等;

如果两个对象不等(x.equals()==false),则它们的hashCode可能相等,也可能不相等。

这一规范导致了一个重要的衍生规范:

如果我们设计的Class重写了equals()方法,也需要重写hashCode()方法。

这一衍生规范还是很好理解的,如果只重写equals()而不重写hashCode(),那么当equals()true时,hashCode不能保证相等,违反了hashCode的基本规范。

比如String中就重写了hashCode():

public int hashCode() {int h = hash;if (h == 0) {    int off = offset;    char val[] = value;    int len = count;            for (int i = 0; i < len; i++) {                h = 31*h + val[off++];            }            hash = h;        }        return h; }
改写后的hashCode()的返回值只与String的具体字符有关,即与其内容有关。

 那么,为什么要对hashCode作出这样的规范呢?

前面的章节中已经探讨过HashtableHashMap的实现,应该感受到了hashCode的重要性。

下面重现一下Hashtable的添加元素方法:

public synchronized V put(K key, V value) {        //确定value非空,若为空,则抛出NullPointerException      if (value == null) {          throw new NullPointerException();      }        // 接下来判断传入的key是否已经存在hashtable中,而这需要判断hashCode是否相等以及equal()是否为true      Entry tab[] = table;            //得到当前key的hash值,如果Key为Null,也将抛出NullPointerException      int hash = key.hashCode();            //根据hash值算出索引      int index = (hash & 0x7FFFFFFF) % tab.length;           //判断table[index]处是否已经非空      for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {           //如果table[index]非空           //判断table[index]处key的hash值与传入的key的hash值是否相等           // 如果二者hash值相等,则判断equals()是否为真          if ((e.hash == hash) && e.key.equals(key)) {                   //如果通过了判断,则说明table[index]处的key和传入的key是相同的,用传入的value替换table[index]的value          V old = e.value;          e.value = value;          return old;          }      }      modCount++;  }

相同的对象(x.equals(y)==true)需要hashhash表的同一个位置,如果相同的对象(x.equals(y)==true)hashCode不同,那么便无法保证这一点。

比如:

String s1 = new String("love");

String s2 = new String("love");

s1.equals(s2)==true,所以这两个对象是相等的,如果此时s1.hashCode()!=s2.hashCode(),那么在添加元素时便可能获得不同的索引,

即相同的对象添加到了hash容器的不同位置。这显然又违反了hash容器的初衷。


原创粉丝点击