2015-04-20 62 views
3

我已經定義了以下嘗試使「IamImmutable.class」不可變的類。但是當初始化IamImmutable後,當我在TestingImmutability.class中更改hashmap值時,這些更改適用於Hashmap。即使我們用新的HashMap(舊)實例化它,HashMap也會引用同一個對象。我需要在實例中使Hashmap不可變。我已經嘗試迭代和複製值,但這不起作用。任何人都可以建議如何進行?帶有散列映射的不可變類的示例

package string; 
import java.util.HashMap; 
import java.util.Map.Entry; 

public final class IamImmutable { 
    private int i; 
    private String s; 
    private HashMap<String, String> h; 

    public IamImmutable(int i, String s, HashMap<String, String> h) { 
    this.i = i; 
    this.s = s; 

    this.h = new HashMap<String, String>(); 
    for (Entry<String, String> entry: h.entrySet()) { 
     this.h.put((entry.getKey()), entry.getValue()); 
    } 
    } 

    public int getI() { 
    return i; 
    } 

    public String getS() { 
    return s; 
    } 

    public HashMap<String, String> getH() { 
    return h; 
    } 
} 

和測試:

package string; 

import java.util.HashMap; 
import java.util.Map.Entry; 

public class TestingImmutability { 

    public static void main(String[] args) { 
    int i = 6; 
    String s = "[email protected]"; 
    HashMap<String, String> h = new HashMap<String, String>(); 

    h.put("Info1", "[email protected]"); 
    h.put("Inf02", "!amCrazy6"); 


    IamImmutable imm = new IamImmutable(i, s, h); 

    h.put("Inf02", "!amCraxy7"); 

    System.out.println(imm.getS() + imm.getI()); 
    for (Entry<String, String> entry: h.entrySet()) 
     System.out.println(entry.getKey() + " --- " + entry.getValue()); 
    } 
} 

預期輸出:

[email protected] John6 
Inf02---!amCrazy6 
[email protected] John 

實際輸出:

[email protected] John6 
Inf02---!amCraxy7 
[email protected] John 
+0

請參閱:http://stackoverflow.com/questions/9043254/how-to-get-a-immutable-collection-from-java-hashmap – slipperyseal

回答

3

您的測試是錯誤的,您正在檢查h的內容,您傳遞給構造函數並稍後修改的地圖,而不是imm.getH()。如果您檢查適當的地圖

for (Entry<String, String> entry : imm.getH().entrySet()) 
     System.out.println(entry.getKey() + " --- " + entry.getValue()); 

東西蠻好看的:

[email protected] 
Info1 --- [email protected] 
Inf02 --- !amCrazy6 

所以你IamImmutable構造已經很好,傳遞到構造函數後面的任何更改原始地圖也不會影響副本你在建造時做的。您也可以使用other HashMap constructor你所說,這是稍微更具可讀性:

public IamImmutable(int i, String s, HashMap<String, String> h) 
{ 
    this.i = i; 
    this.s = s; 
    this.h = new HashMap<String, String>(h); 
} 

這會工作也沒關係。


一個不同的問題是,getH()傳送到內部地圖參考了世界,如果世界改變了參考,事情會出問題。一個簡單的方法來解決那就是應用您在getH()在構造函數中已經使用,以及同一副本的技巧:

public HashMap < String, String > getH() { 
    return new HashMap<String,String>(h); 
} 

還是要返回之前裝點內部地圖:

public Map<String, String> getH() { 
    return Collections.unmodifiableMap(h); 
} 

請考慮使用ImmutableCollections in the guava library來代替。他們已經完成了與代碼相同的工作,但是對效率和易用性有了更多的考慮。構造時的完整副本和獲取映射很笨拙,如果我們知道它不會被修改,那麼標準的基礎HashMap將執行無意義的修改檢查。

+1

所有的好建議。 (我個人非常喜歡使用Guava不可變的東西,並且會直接去找它們。)我還建議他們首先用除了實際的構造函數調用之外的更通用的Map接口替換具體的,可變的HashMap。這樣可以更容易地看到像這樣的替代方案可以被放進去,並且一旦你寫了代碼就可以更容易地做到這一點。 –

2

爲了讓您的課程不可變,getH()必須返回HashMap的副本。否則,getH()的任何調用者都可以修改IamImmutable類的HashMap成員的狀態,這意味着它不是不可變的。

另一種方法是用訪問內部HashMap而不暴露它的方法替換getH()。例如,您可以使用返回HashMap的所有密鑰的方法String[] keys(),以及返回給定密鑰的值的方法String get(String key)

3

退房

<K,V> Map<K,V> java.util.Collections.unmodifiableMap(Map<? extends K,? extends V> m) 

但注意這將創建一個只讀查看到原來的地圖。所以你可能想複製輸入地圖。

http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html?is-external=true#unmodifiableMap%28java.util.Map%29

這應該工作...

public IamImmutable(int i,String s, Map<String,String> h) { 
    this.i = i; 
    this.s = s; 

    Map<String,String> map = new HashMap<String,String>(); 
    map.putAll(h); 
    this.h = Collections.unmodifiableMap(map); 
} 

您可能還可能需要改變成員聲明和get方法的基礎Map類型,而不是HashMap

+0

如果他改變這一點,測試仍然會失敗,因爲測試是錯誤的。 – flup

+0

謝謝..這可能工作,但@flup答案是在我的上下文 – Atom

+0

當然最好的解決方案是修復測試和使用不可修改的地圖,因爲複製地圖並返回相同的可修改的實例,不會使地圖不可變。它只意味着它不會影響原始地圖。這很好,如果這是所有必需的(但它不應該被稱爲不可變的)。 – slipperyseal

0
HashMap <MyKey, MyValue> unmodifiableMap = Collections.unmodifiableMap(modifiableMap); 

使用以上代碼爲不可變對象。

0

首先你的變量聲明是錯誤的。應基本上被宣佈爲final。爲什麼final?所以沒有人可以編寫這些變量的setter方法。另外,你是否做你的HashMap的淺層或深層拷貝並不重要。從不可變類發送clone()可變對象引用的基本規則。

我修改了一下你的班級,做了些微修改。這裏的代碼:

package string; 
import java.util.HashMap; 
import java.util.Map.Entry; 

public final class IamImmutable { 
    private final int i; 
    private final String s; 
    private HashMap<String, String> h; 

    public IamImmutable(int i, String s, HashMap<String, String> h) { 
     this.i = i; 
     this.s = s; 

     // It doesn't matter whether you make deep or shallow copy 
     this.h = new HashMap<String, String>(); 
     for (Entry<String, String> entry : h.entrySet()) { 
      this.h.put((entry.getKey()), entry.getValue()); 
     } 
    } 

    public int getI() { 
     return i; 
    } 

    public String getS() { 
     return s; 
    } 

    @SuppressWarnings("unchecked") 
    public HashMap<String, String> getH() { 
     // Here's the main change 
     return (HashMap<String, String>) h.clone(); 
    } 
} 

而你的測試類也有點不對。我修改了本地和祖先的變化。這裏的測試類:

package string; 
import java.util.HashMap; 

public class TestingImmutability { 

    public static void main(String[] args) { 
     int i = 6; 
     String s = "[email protected]"; 
     HashMap<String, String> h = new HashMap<String, String>(); 

     h.put("Info1", "[email protected]"); 
     h.put("Inf02", "!amCrazy6"); 

     IamImmutable imm = new IamImmutable(i, s, h); 
     System.out.println("Original values : " + imm.getI() + " :: " + imm.getS() + " :: " + imm.getH()); 

     h.put("Inf02", "!amCraxy7"); 
     System.out.println("After local changes : " + imm.getI() + " :: " + imm.getS() + " :: " + imm.getH()); 

     HashMap<String, String> hmTest = imm.getH(); 
     hmTest.put("Inf02", "!amCraxy7"); 
     System.out.println("After ancestral changes : " + imm.getI() + " :: " + imm.getS() + " :: " + imm.getH()); 

    } 
} 

我希望這可以幫助。

乾杯。

相關問題