我在准备SCJP期间发现了这个问题:
class Key {
public int i;
public Key(int i) {
this.i = i;
}
public boolean equals(Object o) {
return i == ((Key) o).i;
}
public int hashCode() {
return i;
}
}
public class Test {
public static void main(String[] args) {
Set<Key> set = new HashSet<Key>();
Key k1 = new Key(1);
Key k2 = new Key(2);
set.add(k1);
set.add(k1);
set.add(k2);
set.add(k2);
System.out.print(set.size() + “:”);
k2.i = 1;
System.out.print(set.size() + “:”);
set.remove(k1);
System.out.print(set.size() + “:”);
set.remove(k2);
System.out.print(set.size());
}
}
结果是:2:2:1:1
。
问题是:为什么在更改k2
字段后无法将其删除?
答案 0 :(得分:3)
HashSet
中的对象根据其hashCode()
定位。如果更改导致hashCode()
更改的字段,Java将无法在集合中找到它,因此无法将其删除。
答案 1 :(得分:2)
如果您想象HashSet
如何运作,那么它就变得显而易见了。
假设您有HashSet
仅允许0
或1
哈希码值,它有两个存储桶:
1
0
当add
项目HashSet
时,HashSet
会确定hashCode
是1
还是0
并找到正确的桶。
因此,您可以创建对象thing
和setHashCode(1)
以及add
。 HashSet
获取hashCode
并将其正式转储到1
广告管理中。
如何拨打thing.setHashCode(0)
然后remove
。怎么了?好吧,HashSet
获得了hashCode
thing
,0
。它会在0
存储桶中查找,但当您将add
thing
调用时,1
被置于存储桶HashSet
中,HashSet
会查找错误的存储桶< / strong>即可。 thing
无法找到add
来删除它。
有趣的是,如果thing2
hashCode
0
为equals
。如果hashCode == other.hashCode
已实施(如您的示例中)remove(thing)
,那么在将hashCode
thing
设置为0
后调用thing2
将会实际上删除 Object
。
这是一个很好的示例,说明如果equals
是hashCode
,Object
和Map
属性中Set
的属性使用的原因不应该更改用作{{1}}密钥或放入{{1}}。