Java 中的不可變集合與不可修改集合
一、簡介
在本教程中,除了 Java 中常見的Collection
類別之外,我們還將研究兩種類型的集合。眾所周知,我們有三個核心集合類別: Map
、 List
和Set
。它們有相應的**U** nmodifiable
和Immutable versions
。
在我們的範例中,我們介紹了 Java 中的Map
集合系列。 Collections.unmodifiableMap()
和Map.of()
方法適用於Map, w
而Collections.unmodifiableList()
、 Collections.unmodifiableSet(), List.of()
和Set.of()
是對應的實用程式方法對於List
和Set
集合類別。相同的概念適用於List
和Set
集合類別。
2. 不可修改的集合
不可修改的集合是可變集合的包裝器,可防止透過包裝器引用進行修改。我們透過使用實用方法來取得不可修改的引用,例如,Java Map
集合中的unmodifiableMap()
:
Map<String, String> modifiableMap = new HashMap<>();
modifiableMap.put("name1", "Michael");
modifiableMap.put("name2", "Harry"
這裡, modifiableMap
是對地圖集合的引用。我們在映射中放置了兩個鍵值對。接下來,我們使用Collection.unmodifiableMap()
實用方法來取得unmodifiableMap
:
Map<String, String> unmodifiableMap = Collections.unmodifiableMap(modifiableMap);
我們得到了一個指向底層集合的新引用unmodifiableMap
。這個不可修改的引用很特殊,因為我們不能使用它來新增或刪除映射中的條目。但它不會影響底層集合或其他引用變數modifiableMap
。我們仍然可以使用初始的modifiableMap
引用在集合中加入更多鍵值對:
modifiableMap.put("name3", "Micky");
集合的變更也將透過新的引用變數unmodifiableMap
反映出來:
assertEquals(modifiableMap, unmodifiableMap);
assertTrue(unmodifiableMap.containsKey("name3"));
現在讓我們嘗試使用unmodifiableMap
引用變數來新增一個條目。預計將不允許該操作並拋出例外:
assertThrows(UnsupportedOperationException.class, () -> unmodifiableMap.put("name3", "Micky"));
modifiableMap
和unModifiableMap
兩個引用指向記憶體中的同一個映射,但它們的行為不同。一個可以自由地在地圖上進行操作,而另一個則不能執行任何透過新增或刪除項目來修改集合的操作。
3. 不可變集合
不可變集合在其整個生命週期中保持不可變,無需對它們進行任何可修改的引用。不可變集合解決了我們能夠使用其他引用修改不可修改集合的問題。要在 Java 中建立Immutable
集合,我們有實用方法Map.of()
或List.of()
。我們創建的任何新引用也始終是不可變的:
@Test
public void givenImmutableMap_WhenPutNewEntry_ThenThrowsUnsupportedOperationException() {
Map<String, String> immutableMap = Map.of("name1", "Michael", "name2", "Harry");
assertThrows(UnsupportedOperationException.class, () -> immutableMap.put("name3", "Micky"));
}
當我們嘗試將條目放入immutableMap
時,我們就會遇到UnsupportedOperationException
異常。 Map.copyOf()
傳回底層映射的引用,該引用也是不可變的:
@Test
public void givenImmutableMap_WhenUsecopyOf_ThenExceptionOnPut() {
Map<String, String> immutableMap = Map.of("name1", "Michael", "name2", "Harry");
Map<String, String> copyOfImmutableMap = Map.copyOf(immutableMap);
assertThrows(UnsupportedOperationException.class, () -> copyOfImmutableMap.put("name3", "Micky"));
}
因此,如果我們想確保沒有其他對我們的集合的引用可以修改它,我們必須使用Java
中的Immutable
集合。
4. 不可變和不可修改集合注意事項
4.1.線程安全
原則上,不可變類別是執行緒安全的,因為多個執行緒可以同時存取它們,而不必擔心更改底層集合。使用不可變集合可以防止多個執行緒覆蓋狀態,從而實現線程安全的設計。線程安全意味著在並發環境中使用時不需要明確同步。這也消除了對任何鎖等的需要,從而簡化了並發編程。
4.2.表現
當我們將不可修改或不可變集合與相應的可變集合進行比較時,它們的表現很差。對於更新,我們無法進行就地更新。相反,我們必須創建物件的新副本。這會增加開銷並導致效能下降。此外,由於建立新實例,與可變實例相比,它們可能具有更高的記憶體使用量。然而,不可變集合在頻繁讀取和不頻繁寫入的場景中表現出色。
4.3.可變對象
我們必須確保新增到不可變集合中的可變物件被防禦性複製,以防止外部修改。多線程上下文需要集合及其包含的可變物件的線程安全性。
5. 結論
在本文中,我們仔細研究了Map
、 List
和Set
等集合類別的Immutable
和Unmodifiable
風格。當我們需要一個集合透過特定引用保持不被修改但仍然希望原始集合是可變的時,不可修改的集合是合適的。另一方面,當我們希望確保不會對集合進行任何修改(即使透過任何引用)時,不可變集合是理想的選擇。此外,我們也討論了一些常見用例。與往常一樣,本文的源代碼可以在 GitHub 上找到。