更改集合中的元素会更改“等于”语义

想象一下,我们有这段代码。

public class HashAddAfter { private class A { public int value; public A(int value) { this.value = value; } public void setValue(int value) { this.value = value; } // Code for hashCode()... // Code for equals()... } Set list1 = new HashSet(); Set list2 = new HashSet(); public static void main(String[] args) { HashAddAfter x = new HashAddAfter(); A e1 = x.new A(1); A e2 = x.new A(1); x.list1.add(e1); x.list2.add(e2); System.out.println(x.list1.equals(x.list2)); // true e1.setValue(4); e2.setValue(4); System.out.println(x.list1.equals(x.list2)); // false } } 

由于空间限制,我没有为hashCode()和equals()设置代码,但它是从Eclipse生成的代码。

问题是在更改两个集合中的元素之前,集合是相等的。 在更改它们的值(每个都为相同的值)之后,这些集不再相等,尽管e1.hashCode()== e2.hashCode()和e1.equals(e2)。

我猜测在比较两个HashSets时,Java使用元素的原始hashCode(插入时的那个)。 因此,在插入后更改元素会更改其原始hashCode,因此contains()将返回false。

在我看来,这是一种非常不直观的行为。

你怎么看?

这正是预期的行为。 Set实现没有可能意识到元素的hashCode已经改变,所以没有什么可以防止这种可能性。

来自Set Javadoc :

注意:如果将可变对象用作set元素,则必须非常小心。 如果在对象是集合中的元素的同时以影响等于比较的方式更改对象的值,则不指定集合的​​行为。 这种禁令的一个特例是,不允许将一个集合作为一个元素包含在内。