【发布时间】:2014-01-13 16:41:35
【问题描述】:
在我看到的所有关于重写 equals 和 hashcode 方法的问题中,人们总是说如果你重写 equals 方法,你应该重写 hashcode 方法,反之亦然,以避免在从 Hash 集合中获取对象时出现问题。
我个人认为反之亦然不适合这个目的,让我们考虑我们使用一个对象(带有属性)作为 HashMap 中的键,我们不需要测试它的两个实例之间的相等性对象。
如果我们以有效的方式(基于属性和其他规则)覆盖 hashcode 方法,那么我们可以拥有唯一的键,在这种情况下,对于 HashMap,我们将在每个桶中拥有唯一的值,而 HashMap 不会使用 equals 方法比较桶中的值,因为我们在每个桶中都有一个值。
合约方:
- 我们的对象的两个实例是相等的(Object.equals 之一)意味着它们具有相同的引用,并且根据我们的 hashcode 方法,hashcode 将是相同的 ==> OK
- 不同的哈希码会导致对象不相等 ==> NOK(在我们的例子中,可能会违反此规则),但由于我们有良好的哈希键(我们的场景目的),因此合约并不重要
我错了吗?
编辑:(经过搜索,下面的答案,并查看HashMap的源代码)
为什么我们还要重写 equals 方法?
初始条件、唯一的哈希码和没有覆盖 equals()
- 使用 get() 方法检索值的问题:
Object.equals() 进行引用比较,该方法用于put和get方法中
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
如果我们不覆盖 equals,如果我们丢失了键的引用,我们将无法从 HashMap 中检索值,例如对其他代码不可见的键引用。 读取 HashMap 的唯一方法是使用 Iterator,并通过比较值来检索准确的对:
First put: [key:Instance1Obj(144756696), value:Person1Obj(“Baron”, “Steven”)]
Second put: [key:Instance2Obj(17488696), value:Person2Obj(“Hewlett”, “Emily”)]
如果我们想根据帐号检索 Baron Steven 的帐号,我们不能只创建一个具有相同帐号的新对象,例如 Instance88Obj(144756696),然后调用 get 方法来检索该值。在这种情况下,我们将得到一个 null 作为结果,因为 get 方法使用基于键引用的比较,即使哈希码相同。
- 使用 put() 方法更新值的问题:
如果 equals 未被覆盖,则通过新的实例键(但具有相同的哈希码)输入新值以更新它,将不起作用,只会添加另一对键/值:
First put: [key:Instance1Obj(144756696), value:Person1Obj(“Baron”, “Seven”)]
Second put: [key:Instance2Obj(144756696), value:Person2Obj(“Baron”, “Steven”)]
通常我们期望用“Steven”替换值“Seven”,但替换不会发生
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<K,V> 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;
}
结论:
==> 如果你覆盖哈希码,也覆盖等于。
==>如果你覆盖equals,也覆盖hashcode。(超出这个话题:))
【问题讨论】:
-
哈希表中的桶数不同,远小于不同哈希码的数量。这意味着,会有多个值的桶。
-
@Thomas - 你不正确。它不会“肯定”中断。事实上,从某种意义上说,它根本不会破裂。阅读答案。
-
hashCode()没有给出桶索引,而是Map实现使用的值作为计算桶索引的基础。
标签: java hashmap key equals hashcode