簡體   English   中英

帶有 hashmap 的不可變類示例

[英]Example of an immutable class with hashmap

我已經定義了以下類,試圖使“IamImmutable.class”不可變。 但是當我在初始化 IamImmutable 后更改 TestingImmutability.class 中的 hashmap 值時,這些更改適用於 Hashmap。 即使我們使用 new HashMap(old) 實例化它,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 = "!am@John";
    HashMap<String, String> h = new HashMap<String, String>();

    h.put("Info1", "!am@John");
    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());
  }
}

預期輸出:

 !am@ John6 Inf02---!amCrazy6 Info1---!am@ John

實際輸出:

 !am@ John6 Inf02---!amCraxy7 Info1---!am@ John

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

另一種方法是將getH()替換為訪問該內部HashMap而不暴露它的方法。 例如,您可以有一個返回HashMap所有鍵的方法String[] keys()和返回給定鍵值的方法String get(String key)

查看

<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

您的測試是錯誤的,您正在檢查h的內容,即您傳遞給構造函數並稍后修改的映射,而不是imm.getH() 如果你檢查正確的地圖

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

事情看起來很好:

!am@John6
Info1 --- !am@John
Inf02 --- !amCrazy6

所以,你的IamImmutable構造已經是很好,以后的任何變化傳遞到構造函數的原始地圖也不會影響抄你在構建時提出。 您還可以使用您提到的其他 HashMap 構造函數,它稍微更具可讀性:

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 他們已經完成了與此代碼所做的相同的工作,但更多地考慮了效率和易用性。 構建時的完整副本和獲取映射是笨拙的,如果我們知道它無論如何都不會被修改,那么標准的底層 HashMap 將進行毫無意義的修改檢查。

首先,您的變量聲明是錯誤的。 本質上應該聲明為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 = "!am@John";
        HashMap<String, String> h = new HashMap<String, String>();

        h.put("Info1", "!am@John");
        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());

    }
}

我希望這有幫助。

干杯。

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

對不可變對象使用上面的代碼。

HashMap在 Java 中實現了Cloneable接口,因此您可以簡單地克隆映射並返回。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM