以下情况如何:
void contains(LinkedHashSet data, Object arg) {
System.out.println(data.getClass()); // java.util.LinkedHashSet
System.out.println(arg.hashCode() == data.iterator().next().hashCode()); // true
System.out.println(arg.equals(data.iterator().next())); // true
System.out.println(new ArrayList(data).contains(arg)); // true
System.out.println(new HashSet(data).contains(arg)); // true
System.out.println(new LinkedHashSet(data).contains(arg)); // true (!)
System.out.println(data.contains(arg)); // false
}
我做错了吗?
显然,它并不总是发生(如果你创建了一组简单的对象,你就不会重现它)。但在我的情况下,它总是会发生更复杂的arg类。
编辑:这里我没有定义arg
的主要原因是它是相当大的类,Eclipse生成的hashCode
跨越20行和{{ 1}}两倍长。而且我不认为它是相关的 - 只要它们对于这两个对象是相同的。
答案 0 :(得分:4)
当您构建自己的对象并计划在集合中使用它们时,您应始终覆盖以下方法:
boolean equals(Object o);
int hashCode();
equals的默认实现检查对象是否指向同一个对象,而您可能希望重新定义它以检查内容。
尽可能合理,Object类定义的hashCode方法确实为不同的对象返回不同的整数。为了遵守规则,对象的hashCode应该相同,所以你也要重新定义hashCode。
编辑:我原本期待hashCode
或equals
实施错误,但自从您的回答以来,您发现您在将密钥添加到HashSet或HashMap。
将一个Object添加到哈希集合时,会计算其hashCode并将其用于将其映射到Collection中的物理位置。
如果用于计算hashCode的某些字段发生了更改,则hashCode本身会发生变化,因此HashSet实现会变得混乱。当它试图获取Object时,它将查看另一个物理位置,并且不会找到Object。如果您枚举该集,则对象仍然存在。
因此,请始终使用HashMap或HashSet键Immutable。
答案 1 :(得分:1)
知道了。一旦你知道了,答案是如此明显,你只能尴尬地脸红。
static class MyObj {
String s = "";
@Override
public int hashCode() {
return s.hashCode();
}
@Override
public boolean equals(Object obj) {
return ((MyObj) obj).s.equals(s);
}
}
public static void main(String[] args) {
LinkedHashSet set = new LinkedHashSet();
MyObj obj = new MyObj();
set.add(obj);
obj.s = "a-ha!";
contains(set, obj);
}
这足以可靠地重现它。
说明: 你永远不要改变用于hashCode()的字段!
答案 2 :(得分:0)
您的问题中似乎缺少某些内容。我做了一些猜测:
private void testContains() {
LinkedHashSet set = new LinkedHashSet();
String hello = "Hello!";
set.add(hello);
contains(set, hello);
}
void contains(LinkedHashSet data, Object arg) {
System.out.println(data.getClass()); // java.util.LinkedHashSet
System.out.println(arg.hashCode() == data.iterator().next().hashCode()); // true
System.out.println(arg.equals(data.iterator().next())); // true
System.out.println(new ArrayList(data).contains(arg)); // true
System.out.println(new HashSet(data).contains(arg)); // true
System.out.println(new LinkedHashSet(data).contains(arg)); // true (!)
System.out.println(data.contains(arg)); // true (!!)
}
编辑:跟踪不断变化的问题!
除了第一个输出外,我仍然可以获得“真实”。请更具体地说明“arg”参数的类型。