使用Map / Set对象图

时间:2017-11-27 11:27:26

标签: java equals hashcode

调查一些特殊情况,其中某些对象不应该与它们相同,并且来到这个简单的测试用例,简化了我的问题。

当使用jdk8u152在Eclipse中使用JUnit运行时,最后一个assertEquals失败,有人可以解释原因吗?

这与Set / HashSet有关,因为如果我将改为,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);
}

3 个答案:

答案 0 :(得分:3)

您正在改变集合的元素。这导致了未指明的行为。

来自JavaDoc

  

如果将可变对象用作集合元素,则必须非常小心。如果在对象是集合中的元素的同时以影响等于比较的方式更改对象的值,则不指定集合的​​行为。

答案 1 :(得分:3)

您要将aobo HashMap添加到HashSet s asbs

稍后你通过在每个中添加一个新条目来改变aobo

这意味着hashCode中用于放置ao的{​​{1}}不再是as的当前hashCode和{{1}用于在ao中放置hashCode的{​​}不再是bo的当前bs

因此,hashCode bo无法在另一个AbstractSet中找到一个equals的元素,因此得出Set不是Set的结论等于as。因此bs不等于a

以下是b AbstractSet的实施情况。您可以看到它使用equals,而containsAll依次调用contains(),它依赖于搜索元素的hashCode。由于在将元素添加到hashCodeSet已更改,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;
    }
}

如果以影响HashSetequals结果的方式改变hashCode的元素,则必须在HashSet之前删除该元素。更新后更新并再次添加。

添加以下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);

答案 2 :(得分:-1)

这是因为HashMap的hascode实现基本上是x或键和值。如果key或value为null,则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;
    }

添加键值对后,哈希码值会发生变化。