2009-10-29 80 views

回答

0

看了你的問題更具體:

您不能Set.add添加重複,從Java文檔()或你的意思是?:的addAll

添加指定的元素如果此集合尚未出現(可選操作)。更正式地說,如果該集合不包含元素e2(e == null?e2 == null:e.equals(e2)),則將指定的元素e添加到該集合中。如果此集合已包含該元素,則該呼叫將保持集合不變並返回false。結合對構造函數的限制,這可以確保集合永遠不會包含重複的元素。

14

實際上,來自java中的源代碼最多的Set實現的AFAIK甚至不檢查元素是否已被包含。

他們總是在其內部結構上執行add(),該內部結構持有set元素並讓該對象處理重複情況。

例如HashSet在內部HashMap上調用put(K,V),它只是插入新對象覆蓋舊條目(如果重複)。

10

讀一點你的問題我猜你看到奇怪的行爲與java.util.HashSet(通常是每個人默認使用)。

Contary到java.util.Set合同有可能得到一個java.util.HashSet同樣的對象兩次這樣的:

import java.util.HashSet; 
import java.util.Set; 

public class SetTest 
{ 
    public static void main(String[] args) 
    { 
    MyClass myObject = new MyClass(1, "testing 1 2 3"); 

    Set<MyClass> set = new HashSet<MyClass>(); 
    set.add(myObject); 

    myObject.setHashCode(2); 
    set.add(myObject); 

    System.out.println(set.size()); // this will print 2. 
    } 

    private static class MyClass 
    { 
    private int hashCode; 
    private String otherField; 

    public MyClass(int hashCode, String otherField) 
    {  
     this.hashCode = hashCode; 
     this.otherField = otherField; 
    } 

    public void setHashCode(int hashCode) 
    { 
     this.hashCode = hashCode; 
    } 

    public boolean equals(Object obj) 
    {  
     return obj != null && obj.getClass().equals(getClass()) && ((MyClass)obj).otherField.equals(otherField); 
    } 

    public int hashCode() 
    { 
     return hashCode; 
    } 
    } 
} 

從@jitter指針,看看源後,你可以看到爲什麼這會發生。

像@jitter說的,java.util.HashSet在內部使用java.util.HashMap。當散列在第一個和第二個之間變化時加入java.util.HashMap中使用不同的桶並且該對象在該集中兩次。

代碼示例可能看起來有點受損,但我已經看到這種情況發生在域類中,其中哈希是從可變字段創建的,並且equals方法未與這些字段保持同步。

+2

以修改hashCode()/ equals()結果的方式修改HashSet中的對象會產生未定義的行爲。 – 2009-10-29 09:34:12

+1

@Joachim - 確切地說,但並不意味着它不會發生。事實上,流行的IDE生成的equals/hashCode方法通常會導致hashCode隨着對象的變化而改變。 – 2009-10-29 09:45:23

+4

如果對象發生了變異,hashcode *應該改變 - 畢竟它需要與equals()一致,所以如果一個對象不再被認爲等於它的預突變狀態,它就需要改變。這裏真正的問題是使用可變對象作爲HashMap鍵; * *建議只使用不可變的對象,否則你打開自己的這種隨機性,因爲一般來說''hashCode()'*必須*隨着可變對象的變化而改變。 – 2009-10-29 10:36:26

2

一個簡單的方法來找出這是從源頭爲您感興趣的代碼查找。

每個JDK已經包含了src.zip其中包含了公共類的源代碼,這樣你可以只找到HashSet的源代碼並看看:)我經常使用Eclipse進行此操作。啓動它,創建一個新的Java項目,將JVM設置爲已安裝的JDK(如果不是,則使用系統默認的JRE,它不包含src.zip),並將Ctrl-Shift-T設置爲HashSet。