2013-05-06 78 views
3

想象一下,我们有这段代码。更改集合中的元素会改变'等于'语义

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<A> list1 = new HashSet<A>(); 
    Set<A> list2 = new HashSet<A>(); 

    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。

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

您认为如何?

回答

2

这正是预期的行为。 Set实现没有办法知道元素的hashCode已经改变,所以没有办法防御这种可能性。

SetJavadoc

注意:如果使用可变对象作为一组元素大,一定要小心。如果对象的值以影响等于比较的方式更改,而对象是集合中的元素,则不会指定集的行为。这种禁令的一个特例是,一个集合不允许自己作为一个元素。