调查一种特殊情况,其中某些对象不等于应有的位置,并得出了这个简单的测试用例,简化了我的问题。
当在 Eclipse 中使用 jdk8u152 运行它时,最后一个断言等于失败,谁能解释为什么?
这是Set/HashSet的东西,因为如果我将as,bs更改为ArrayList的,而不是最终的assertEquals。
@Test
public void test()
{
String list = "list";
String object = "object";
String value = "value";
Map<String, Object> a = new HashMap<>();
Map<String, Object> b = new HashMap<>();
assertEquals(a, b);
Set<Object> as = new HashSet<>();
Set<Object> bs = new HashSet<>();
a.put(list, as);
b.put(list, bs);
assertEquals(a, b);
Map<String, Object> ao = new HashMap<>();
as.add(ao);
Map<String, Object> bo = new HashMap<>();
bs.add(bo);
assertEquals(a, b);
ao.put(object, value);
bo.put(object, value);
assertEquals(a, b);
}
你正在改变集合的元素。 这会导致未指明的行为。
来自JavaDoc:
如果将可变对象用作集合元素,则必须格外小心。如果对象的值以影响等于比较的方式更改,而对象是集中的元素,则不指定集合的行为。
您正在将ao
和bo
HashMap
s添加到HashSet
的as
和bs
中。
稍后,您可以通过在每个条目中放置一个新条目来改变ao
和bo
。
这意味着用于将ao
放置在as
中的hashCode
不再是ao
的当前hashCode
,而用于将bo
放置在bs
中的hashCode
不再是bo
的当前hashCode
。
结果,AbstractSet
的equals
不能在另一个Set
中定位一个Set
的元素,所以它得出结论as
不等于bs
。因此a
不等于b
。
这是AbstractSet
equals
的实现。你可以看到它使用containsAll
,而contains()
又调用 ,它依赖于搜索元素的hashCode
。由于该hashCode
在元素被添加到Set
后发生了变化,contains()
找不到该元素。
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection<?> c = (Collection<?>) o;
if (c.size() != size())
return false;
try {
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
如果以影响equals
或hashCode
结果的方式更改HashSet
的元素,则必须在更新之前从HashSet
中删除该元素,并在更新后再次添加该元素。
添加以下remove
和add
调用将导致最终a
等于b
:
....
assertEquals(a, b);
bs.remove (bo); // added
as.remove (ao); // added
ao.put(object, value);
bo.put(object, value);
as.add (ao); // added
bs.add (bo); // added
assertEquals(a, b);
这是因为HashMap的hascode实现基本上是键和值的x-or。如果键或值为空,则 hascode 将为零。因此,所有空哈希图的哈希码都将为零。
/*hashcode of HashMap*/
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
/*hashcode of object*/
public static int hashCode(Object o) {
return o != null ? o.hashCode() : 0;
}
添加键值对后,哈希码值会发生变化。