我刚做了以下代码:
import java.util.HashSet;
import java.util.Set;
public class MyClass {
private static class MyObject {
private int field;
public int getField() {
return field;
}
public void setField(int aField) {
field = aField;
}
@Override
public boolean equals(Object other) {
boolean result = false;
if (other != null && other instanceof MyObject) {
MyObject that = (MyObject) other;
result = (this.getField() == that.getField());
}
return result;
}
@Override
public int hashCode() {
return field;
}
}
public static void main(String[] args) {
Set<MyObject> mySet = new HashSet<MyObject>();
MyObject object = new MyObject();
object.setField(3);
mySet.add(object);
object.setField(5);
System.out.println(mySet.contains(object));
MyObject firstElement = mySet.iterator().next();
System.out.println("The set object: " + firstElement + " the object itself: " + object);
}
}
打印:
false
The set object: MyClass$MyObject@5 the object itself: MyClass$MyObject@5
基本上意味着object
不被认为是在集合中,而它的实例本身就是在集合中。这意味着如果我在一个集合中插入一个对象,然后更改参与hashCode
方法计算的字段的值,那么HashSet
方法将按预期工作。这不是可能错误的根源吗?有人可以如何防范此类案件?
答案 0 :(得分:4)
以下是Set API的引用。它解释了一切。
注意:如果将可变对象用作set元素,则必须非常小心。如果在对象是集合中的元素的同时以影响等于比较的方式更改对象的值,则不指定集合的行为。这种禁止的一个特例是,不允许集合将自己作为一个元素包含在内。
答案 1 :(得分:2)
HashSet
已在HashMap
上实施。
HashMap
缓存hashCode
的{{1}},因此如果您更改key
,即使哈希函数将hashCode
映射到同一个存储桶作为原始对象存在,但它找不到因为在检查对象相等之前它将检查hashCode
。
见行:
hashCode
如果if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
通过散列函数映射到不同的桶,而不是原始对象,那么它显然无法找到。
因此,即使更改hashCode hashSet也无法找到相同的对象。希望能帮助到你。
因此,HashMap或您放入HashSet的对象的键应该是不可变的或有效的不可变的。
@fazomisiek
hashCode
同样,如果你检查HashSet的来源,你可以找到它。
答案 2 :(得分:0)
此问题仅限于java.util.HashSet
和基础java.util.HashMap
的实施。从根本上说,你可以修改集合中元素的能力,以获得更快的插入/查找性能 - 这只是使用散列集/映射数据结构的合同的一部分。
如果你不能保证每个人都记得他们不能修改集合中的对象,那么绝对防止这种情况发生的唯一方法就是只在第一时间将不可变对象插入到集合中。