为什么HashMap.put比较哈希和测试相等?
我用Java分析HashMap
源代码并得到关于put
方法的问题。
下面是JDK1.6中的put
方法:
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
我对if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
为什么这样的情况?
因为在Java超类Object
,有一个hashCode
和equals
的契约 :
如果两个对象根据equals(Object)方法相等,则对两个对象中的每一个调用hashCode方法必须生成相同的整数结果。
所以key.equals(k)
意味着key.hashCode() == k.hashCode()
。
hash()
如下:
static int hash(int h) { // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
因此key.hashCode() == k.hashCode()
意味着e.hash == hash
。
那么为什么不是if ((k = e.key) == key || key.equals(k))
?
这只是一个优化:比较两个整数比调用equals()
更快。
如果两个哈希码不同,那么,基于equals
和hashCode
的契约,地图知道现有密钥不等于给定密钥,并且可以更快地到达下一个密钥。
它只是避免了方法调用:如果散列(不是hashCode()
,它是地图自己的散列)与条目的散列不同,它知道它根本不必调用equals
。 只是优化了一下。
‘hash’变量的值可以与keys hashcode不同。 ‘hash’变量是调用’hash(key.hashCode())’方法的结果。 因此,需要比较哈希值和键的相等性。