迭代 Guava Multimap
1. 概述
在本文中,我們將研究 Google Guava 函式庫中的Map
實作之一 - Multimap
。它是一個將鍵映射到值的集合,類似於java.util.Map
,但其中每個鍵可以與多個值關聯。
2. 依賴關係
首先,讓我們將 Guava 依賴項新增到pom.xml
中:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.3.0-jre</version>
</dependency>
最新版本可以在這裡找到。
3. Multimap
實現
在 Guava Multimap,
如果我們為同一個鍵加兩個值,第二個值不會覆蓋第一個值。相反,我們將在結果map
中有兩個值。我們來看一個測試用例:
String key = "a-key";
Multimap<String, String> map = ArrayListMultimap.create();
map.put(key, "firstValue");
map.put(key, "secondValue");
assertEquals(2, map.size());
列印map
的內容將會輸出:
{a-key=[firstValue, secondValue]}
當我們透過鍵「 a-key
」來取得值時,我們得到包含「 firstValue
」和「 secondValue
」的Collection<String>
結果:
Collection<String> values = map.get(key);
列印值將輸出:
[firstValue, secondValue]
4. 迭代Multimap
通常, Multimap
類別提供了幾個內建方法,我們可以使用它們來迭代其內容。那麼,讓我們深入研究每個選項。
在我們深入研究具體細節之前,讓我們先創建一個Multimap
並用資料填充它:
Multimap<String, String> multiMap = ArrayListMultimap.create();
multiMap.putAll("key1", List.of("value1", "value11", "value111"));
multiMap.putAll("key2", List.of("value2", "value22", "value222"));
multiMap.putAll("key3", List.of("value3", "value33", "value333"));
4.1.使用entries()
讓我們從最簡單的解決方案開始,其中涉及使用entries()
方法。顧名思義,它會傳回給定Multimap
中包含的所有鍵值對的視圖集合。
那麼,讓我們看看它的實際效果:
static void iterateUsingEntries(Multimap<String, String> multiMap) {
multiMap.entries()
.forEach(entry -> LOGGER.info("{} => {}", entry.getKey(), entry.getValue()));
}
執行該方法將輸出:
key1 => value1
key1 => value11
key1 => value111
key2 => value2
key2 => value22
key2 => value222
key3 => value3
key3 => value33
key3 => value333
在這裡,我們使用forEach()
方法來迭代提取的條目。每個條目表示一個Map.Entry
實例並保存鍵值對資訊。
4.2.使用asMap()
或者,我們可以使用asMap()方法來實現相同的結果。此方法傳回指定Multimap
的Map
視圖**:
static void iterateUsingAsMap(Multimap<String, String> multiMap) {
multiMap.asMap()
.entrySet()
.forEach(entry -> LOGGER.info("{} => {}", entry.getKey(), entry.getValue()));
}
這是輸出:
key1 => [value1, value11, value111]
key2 => [value2, value22, value222]
key3 => [value3, value33, value333]
正如我們所看到的,我們使用entrySet()
方法從傳回的Map
中提取條目Set
。然後,我們使用forEach()
方法迭代它們。
4.3.使用keySet()
如果我們只想迭代鍵,那麼keySet()
方法是另一個可以考慮的選項。它將特定Multimap
中包含的所有不同鍵作為Set
返回:
static void iterateUsingKeySet(Multimap<String, String> multiMap) {
multiMap.keySet()
.forEach(LOGGER::info);
}
上述方法將會列印:
key1
key2
key3
正如我們在上面看到的,我們使用方法引用而不是 lambda 表達式來顯示提取的鍵。
4.4.使用keys()
同樣,我們可以使用keys()
方法來達到相同的目的。此方法傳回一個視圖集合,其中包含每個鍵值對中的鍵:
static void iterateUsingKeys(Multimap<String, String> multiMap) {
multiMap.keys()
.forEach(LOGGER::info);
}
與傳回不同鍵的keySet(),
不同,此方法為每個值顯示一個鍵。話雖如此,它會列印每個鍵三次:
key1
key1
key1
key2
key2
key2
key3
key3
key3
簡而言之, keys()
傳回一個Multiset
實例,與傳回 Set 的keySet()
不同Set.
主要區別在於Multiset
接受重複元素。
4.5.使用values()
最後, Multimap
提供values()
方法來取得包含指定Multimap
值的集合:
static void iterateUsingValues(Multimap<String, String> multiMap) {
multiMap.values()
.forEach(LOGGER::info);
}
此方法將記錄每個鍵的值:
value1
value11
value111
value2
value22
value222
value3
value33
value333
我們應該注意到,更新返回的集合也會更新底層的Multimap
,反之亦然。但是,無法為集合新增元素。
5. 與標準Map
的比較
java.util
套件中的標準映射不允許我們將多個值分配給同一個 key 。讓我們考慮一個簡單的情況,當我們使用相同的鍵put()
兩個值放入Map
時:
String key = "a-key";
Map<String, String> map = new LinkedHashMap<>();
map.put(key, "firstValue");
map.put(key, "secondValue");
assertEquals(1, map.size());
由於第二個put()
運算覆蓋了第一個值,因此產生的map
只有一個元素( “secondValue”),
如果我們想要實作與 Guava 的Multimap
相同的行為,
我們需要建立一個具有List<String>
作為值類型的Map
:
String key = "a-key";
Map<String, List<String>> map = new LinkedHashMap<>();
List<String> values = map.get(key);
if(values == null) {
values = new LinkedList<>();
values.add("firstValue");
values.add("secondValue");
}
map.put(key, values);
assertEquals(1, map.size());
顯然,它使用起來不太方便,如果我們的程式碼中有這樣的需求,那麼 Guava 的Multimap
可能是比java.util.Map.
這裡要注意的一件事是,雖然我們有一個包含兩個元素的列表,但size()
Map,
Multimap, size()
keySet().size()
1。 keySet().size()
傳回不同鍵的數量。
6. Multimap
的優點
多重映射通常用在原本會出現Map<K, Collection<V>>
的地方。差異包括:
- 在使用
put()
新增條目之前無需填入空集合 -
The get() method
永遠不會傳回null
,只傳回一個空集合(我們不需要像Map<String, Collection<V>>
測試案例中那樣檢查null
) - 當且僅當鍵映射到至少一個值時,該鍵才包含在
Multimap
中。任何導致鍵具有零關聯值的操作,都會具有從Multimap
中刪除該鍵的效果(在Map<String, Collection<V>>,
即使我們從集合中刪除所有值,我們仍然保留一個空Collection
作為一個值,這是不必要的記憶體開銷) - 總條目值計數可透過
size()
取得 -
Multimap
附帶多種即用型方法,讓迭代邏輯變得簡單實用
七、結論
在本文中,我們了解如何以及何時使用 Guava Multimap.
我們將其與標準java.util.Map
進行了比較,並看到了 Guava Multimap.
在此過程中,我們探索了迭代給定Multimap
的不同方法。
所有這些範例和程式碼片段都可以在 GitHub 上找到。