2013-05-06 25 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

注意:如果使用可變對象作爲一組元素大,一定要小心。如果對象的值以影響等於比較的方式更改,而對象是集合中的元素,則不會指定集的行爲。這種禁令的一個特例是,一個集合不允許自己作爲一個元素。