我知道这个问题已经问过很多次了,但是我找不到我的问题的确切答案。
在有效Java的第3章中,有一个场景展示并解释了为什么hashcode应该与equals方法一起覆盖。我了解了大部分内容,但有一部分我无法理解。
那里有一个给定的类,它覆盖了equals方法,但没有覆盖hashCode方法。该对象作为键放在映射中
Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();
m.put(new PhoneNumber(707, 867, 5309), "Jenny");
我理解,如果我们get
使用另一个相等的对象(m. get(new PhoneNumber(707,867,5309))
),它将返回null,仅仅是因为它们的hashcode没有被覆盖以返回相等对象的相等值(因为它将在另一个桶中搜索对象,因为不同的hashcode)。
但是根据我的理解,在那种情况下,不能保证两个对象的hashcode总是返回不同的。如果它们碰巧返回相同的hashcode呢?
我想这部分已经解释过了
即使两个实例碰巧散列到同一个桶,get方法几乎肯定会返回null,因为HashMap有一个优化,缓存与每个条目关联的哈希码,并且如果哈希码不匹配,则无需检查对象相等性。
我只是不明白缓存的事情。有人能详细解释一下吗?
还有,我已经做了家庭作业,发现了一个相关的问题
缓存与其get方法的每个条目关联的哈希码的HashMap优化的影响
但是我对接受的答案不太满意,回答者在评论中说
哈希码可以是任意int,因此每个哈希码不能有自己的桶。因此,一些具有不同哈希码的对象最终会在同一个桶中。
我完全不同意。据我所知,不同的哈希码永远不会在同一个桶中结束。
看看java. util.HashMap如何通过hashCode计算键的桶号:
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
return h & (length-1);
}
如果哈希表长度=16,那么128和256都将进入桶#0。哈希表是一个条目数组:
Entry<K,V>[] table
...
class Entry<K,V> {
K key;
V value;
Entry<K,V> next;
int hash;
...
条目可能会形成一个链(LinkedList
)。如果bucket#0(table[0]
)为空(null
),则新条目将直接放置在那里,否则HashMap将找到链中的最后一个条目并设置最后一个条目的next=new条目。
当说“即使两个实例碰巧散列到同一个桶”时,这并不意味着它们具有相同的哈希码。即使是不同的哈希码也可以映射到同一个桶[阅读关于哈希]。
因此,即使键散列到同一个桶,也可能不会为相关元素调用. equals(由于缓存优化)(因为甚至哈希码都不匹配)。因此,即使相关元素驻留在同一个桶中,它也可能永远不会通过.equals进行比较,因此无法“找到”。