直接来自this java doc:
这一禁令的一个特例是a不允许 map将自己包含为键。虽然地图允许 将自己视为一种价值,建议极端谨慎:等于和 hashCode方法不再在这样的地图上很好地定义。
为什么hashcode和equals不再在这样的地图上得到很好的定义?
答案 0 :(得分:5)
相关部分形成了大多数Map实现使用的AbstractMap.equals:
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key))) // would call equals on itself.
return false;
}
}
将地图添加为值会导致无限循环。
答案 1 :(得分:2)
Java Docs段落的完整引用是:
注意:如果将可变对象用作地图键,则必须非常小心。如果在对象是地图中的键的情况下以影响等于比较的方式更改对象的值,则不指定映射的行为。此禁止的特殊情况是不允许将自己包含为关键字的地图。虽然允许地图将自己包含为值,但建议极其谨慎:在这样的地图上不再定义equals和hashCode方法。
AbstractMap.hashCode()方法使用映射中键值对的哈希码来计算哈希码。因此,每次修改地图时,从此方法生成的哈希码都会更改。
哈希码用于计算存储桶以放置新条目。如果映射本身用作密钥,那么每次更新/删除/修改新条目时计算的存储桶都会不同。因此,将来使用映射作为键的查找很可能会失败,因为从哈希码计算出不同的桶。 Future puts可能无法检测到密钥已经存在于映射中,然后允许具有相同密钥的多个条目(但在不同的桶中)
答案 2 :(得分:1)
如果相同的键映射了相同的值,则两个映射相等。 (在某些实现中。)因此,为了检查相等性,应检查每个成员的相等性。
因此,如果地图包含自身,您将获得无限的等式检查递归。
哈希也是如此,因为可以根据地图中元素的哈希值来计算这些哈希值。
示例:
Map<Int, Object> ma;
Map<Int, Object> mb;
Map<Int, Object> mc;
ma.put(1, ma);
ma.put(2, mb);
mc.put(1, ma);
mc.put(2, mb);
作为一个人,我们可以看到ma
和mc
与定义相同。计算机将在两个地图中看到mb(空地图)上的2个地图,这很好。它将在mc和ma中的另一个地图上看到1个地图。它检查这些地图是否相等。为了确定这一点,它再次检查1的两个值是否等于。再一次。
请注意,并非所有实现都是如此。某些实现可能会检查内存中保存对象的位置是否相等,但是每次递归检查都会无限循环。
答案 3 :(得分:0)
试图解释一下:
equals方法将迭代两个Maps并调用每个键的equals方法和map的值。因此,如果地图包含自身,您将无限期地继续调用equals方法。
哈希码也会发生同样的事情。
来源:AbstractMap类的源代码