####关于Object类的hashCode方法 //2016-08-19 16:00 在Object类的文档上是这么说hashcode()的.
The general contract of hashCode is: 1.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.
2.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.
3.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 hash tables.
1.无论什么时候一个对象的hashcode方法被调用多少次, 只要那些被用于equals比较的信息没有改变.那么就应该是同样的结果.
2.如果两个对象通过***equals被认为相等,那么他们的hashcode也必须相等.***
3.如果两个对象被认为不equal,那么他们的hashCode***不用非得不相等***.
- 第一条没什么可说的,但是第二三条却又是为什么呢?
- 好比无序的Set集合是怎么知道内部元素有没有重复的呢?
通过看代码可以发现HashSet其实内部实现就是一个HashMap. 这里看一下contains方法的实现.
//java.util.HashSet.java transient HashMap> backingMap; @Override public boolean contains(Object object) { return backingMap.containsKey(object); }//java.util.HashMap.javaOverride public boolean containsKey(Object key) { if (key == null) { return entryForNullKey != null; } int hash = Collections.secondaryHash(key); HashMapEntry [] tab = table; for (HashMapEntry e = tab[hash & (tab.length - 1)]; e != null; e = e.next) { K eKey = e.key; if (eKey == key || (e.hash == hash && key.equals(eKey))) { return true; } } return false;}
大致来说就是HashMap通过key的hashCode值来确定这个元素在内部存储的位置.
那么问题来了,根据文档上的规定***两个对象equals相等必须hashcode也相等***. 如果现在像这个情况,对象的equals相同但是hashcode不同会怎么样?
A.equals(B) && A.hashCode() != B.hashCode()
因为A.equals(B)你就认为A和B是一样的了.
map.put(A)map.get(A)map.get(B)
//java.lang.Object.java public int hashCode() { int lockWord = shadow$_monitor_; final int lockWordMask = 0xC0000000; // Top 2 bits. final int lockWordStateHash = 0x80000000; // Top 2 bits are value 2 (kStateHash). if ((lockWord & lockWordMask) == lockWordStateHash) { return lockWord & ~lockWordMask; } return System.identityHashCode(this); }// java.lang.System.java public static native int identityHashCode(Object anObject);
注意如果你没重写对象的hashCode方法,那么它就是直接继承自Object类, 那么这里的map.get(B)就会返回null. 但是你明明已经put进map了(因为A.equals(B)) 所以为了避免这种蛋疼的情况, 你就应该保证两个对象equals相等那么hashcode也应该相等.
***如果两个对象被认为不equal,那么他们的hashCode不用非得不相等. ***
反过来看,即两个不equals的对象但是hashCode相同了. 也就是所谓的的hash碰撞. 下面以HashMap的代码来看看java是怎么处理这个问题的.
public V get(Object key) { Nodee; return (e = getNode(hash(key), key)) == null ? null : e.value; } /** * Implements Map.get and related methods * * [@param](https://my.oschina.net/u/2303379) hash hash for key * [@param](https://my.oschina.net/u/2303379) key the key * [@return](https://my.oschina.net/u/556800) the node, or null if none */ final Node getNode(int hash, Object key) { Node [] tab; Node first, e; int n; K k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) { if (first instanceof TreeNode) return ((TreeNode )first).getTreeNode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }static class Node implements Map.Entry { final int hash; final K key; V value; Node next;}
可以看到Node是一个单链表,如果发生hash碰撞了就需要挨个遍历. 但是如果没有hash碰撞,就可以直接返回. 所以应该尽可能的保证两个对象不一样,hashcode也不一样.