简体   繁体   English

Map / Set对象图的奇怪equals()结果

[英]Weird equals() result with Map/Set object graph

Investigating a special case where some objects didn't equal as they should and came to this simple test case that simplifies my issue. 研究一种特殊情况,其中某些对象不符合其应有的条件,并进入了这个简单的测试用例,简化了我的问题。

When running this with JUnit in Eclipse with jdk8u152 the last assertEquals fails, can anyone explain why? 在带有jdk8u152的Eclipse中使用JUnit运行此命令时,最后一个assertEquals失败,有人可以解释为什么吗?

It's something with Set/HashSet because if I change as,bs to be ArrayList's instead the final assertEquals goes through. 这与Set / HashSet有关,因为如果我将as更改 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);
}

You're mutating the elements of the sets. 您正在变异集合的元素。 That leads to unspecified behaviour. 这导致了未指明的行为。

From the JavaDoc : JavaDoc

Great care must be exercised if mutable objects are used as set elements. 如果将可变对象用作集合元素,则必须格外小心。 The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set. 如果对象的值更改为影响相等比较的方式,而该对象是集合中的元素,则不指定集合的​​行为。

You are adding ao and bo HashMap s to the HashSet s as and bs . 你加入aobo HashMap年代到HashSet小号asbs

Later you mutate ao and bo by putting a new entry in each of them. 稍后,通过在每个bo放置一个新条目来对aobo进行突变。

This means that the hashCode that was used to place ao in as is no longer the current hashCode of ao , and the hashCode that was used to place bo in bs is no longer the current hashCode of bo . 这意味着hashCode这是用来放置aoas不再是当前hashCodeao ,以及hashCode这是用来放置bobs不再是当前hashCodebo

As a result, AbstractSet 's equals cannot locate the element of one Set in the other Set , so it concludes that as is not equal to bs . 其结果是, AbstractSetequals无法找到一个的元件Set在另一个Set ,所以它的结论是, as不等于bs As a result a is not equal to b . 结果a不等于b

Here's the implementation of AbstractSet 's equals . 这是AbstractSet equals You can see that it uses containsAll , which in turns calls contains() , which relies on the hashCode of the searched element. 您可以看到它使用了containsAll ,它依次调用contains() ,后者依赖于搜索到的元素的hashCode Since that hashCode has changed after the element was added to the Set , contains() doesn't find the element. 由于在将元素添加到Set之后该hashCode发生了变化, 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;
    }
}

If you mutate an element of a HashSet in a way that affects the result of equals or hashCode , you must remove the element from the HashSet prior to the update and add it again after the update. 如果以影响equalshashCode结果的方式对HashSet的元素进行变异,则必须在更新之前从HashSet删除该元素,然后在更新后再次添加它。

Adding the following remove and add calls will cause a to be equal to b in the end: 添加以下removeadd调用将最终使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);

That is because of the hascode implementation of HashMap which is basically x-or of key and value. 这是因为HashMap的hascode实现基本上是键和值的x或。 If key or value is null then hascode will be zero. 如果key或value为null,则hascode将为零。 Hence all empty hashmaps will have hashcode as zero. 因此,所有空哈希图的哈希码都将为零。

/*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;
    }

Upon adding key value pairs the hashcode value changes. 在添加键值对后,哈希码值会更改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM