在 Java 中將兩個列表組合成一個映射
一、概述
在 Java 中工作時,通常有兩個需要關聯的單獨列表。換句話說,我們有兩個列表,一個包含鍵,另一個包含值。然後我們想要得到一個Map
,它將鍵列表的每個元素與值列表中的相應元素相關聯。
在本教程中,我們將探索如何以不同的方式實現這一目標。
二、問題簡介
像往常一樣,讓我們通過一個例子來理解這個問題。假設我們有兩個列表:
final List<String> KEY_LIST = Arrays.asList("Number One", "Number Two", "Number Three", "Number Four", "Number Five");
final List<Integer> VALUE_LIST = Arrays.asList(1, 2, 3, 4, 5);
現在,我們想將上面的兩個列表與Map
相關聯。但首先,讓我們初始化一個包含預期鍵值對的HashMap
:
final Map<String, Integer> EXPECTED_MAP = new HashMap<String, Integer>() {{
put("Number One", 1);
put("Number Two", 2);
put("Number Three", 3);
put("Number Four", 4);
put("Number Five", 5);
}};
如上面的代碼所示,合併兩個列表的規則非常簡單。接下來,讓我們看看如何實現這一目標。
3.關於驗證的一句話
現在我們理解了這個問題,我們可能已經意識到給定的兩個列表必須包含相同數量的元素,比如KEY_LIST
和VALUE_LIST
。然而,在實踐中,由於我們無法預測我們所獲得的數據質量,因此兩個給定的列表可能具有不同的大小。如果是這種情況,我們必須按照要求進行進一步的操作。通常,可能有兩種選擇:
- 拋出異常併中斷關聯操作
- 將不匹配問題報告為警告並繼續創建
Map
對像以僅包含匹配的元素
我們可以使用一個簡單的if
檢查來實現它:
int size = KEY_LIST.size();
if (KEY_LIST.size() != VALUE_LIST.size()) {
// throw an exception or print a warning and take the smaller size and continue:
size = min(KEY_LIST.size(), VALUE_LIST.size());
}
// using the size variable for further processings
為簡單起見,我們假設這兩個列表始終具有相同的大小,並在進一步的代碼示例中省略此驗證。此外,我們將使用單元測試斷言來驗證該方法是否返回預期結果。
4. 循環填充Map
由於兩個輸入列表的大小相同,我們可以將這兩個列表與一個循環相關聯。接下來,讓我們看看它是如何完成的:
Map<String, Integer> result = new HashMap<>();
for (int i = 0; i < KEY_LIST.size(); i++) {
result.put(KEY_LIST.get(i), VALUE_LIST.get(i));
}
assertEquals(EXPECTED_MAP, result);
如上例所示,我們創建了一個名為result
的新HashMap
。然後我們使用for
循環遍歷KEY_LIST
中的每個元素,對於每個元素,我們使用相同的索引i
從VALUE_LIST
中檢索相應的元素。然後, put()
方法將鍵值對填充到result
映射中。
5. 使用流 API
Stream API 提供了許多簡潔高效的方法來操作 Java 集合。那麼接下來,讓我們使用 Java Stream API 來關聯兩個列表:
Map<String, Integer> result = IntStream.range(0, KEY_LIST.size())
.boxed()
.collect(Collectors.toMap(KEY_LIST::get, VALUE_LIST::get));
assertEquals(EXPECTED_MAP, result);
正如我們在上面的代碼中看到的, IntStream.range()
方法生成一個從0
到KEY_LIST.
值得一提的是**IntStream
是一個原始流**。因此,我們使用boxed()
方法將IntStream
轉換為Stream<Integer>
,這允許我們使用collect()
方法將元素收集到Map.
6. 使用Iterator
我們已經學習了兩種方法來關聯兩個列表並得到一個Map
作為結果。但是,如果我們仔細研究這兩個解決方案,我們會發現這兩種方法都使用了List.get()
方法。換句話說,我們在建立關聯時調用List.get(i)
通過索引訪問元素。這稱為隨機訪問。
如果我們的列表是ArrayList
,這可能是最常見的情況,則數據由數組支持。因此,隨機訪問速度很快。
但是,如果給定兩個大的LinkedList,
按索引訪問元素可能會很慢。這是因為**LinkedList
需要從列表的開頭迭代到所需的索引**。
因此,使用Iterator
可能是一種更有效的遍歷列表的方法,尤其是對於大型列表:
Map<String, Integer> result = new HashMap<>();
Iterator<String> ik = KEY_LIST.iterator();
Iterator<Integer> iv = VALUE_LIST.iterator();
while (ik.hasNext() && iv.hasNext()) {
result.put(ik.next(), iv.next());
}
assertEquals(EXPECTED_MAP, result);
在此示例中,我們創建了兩個Iterator
對象,每個對像對應一個列表。然後,我們使用while
循環同時遍歷兩個列表,使用每個Iterator
的next()
方法檢索列表中的下一個元素。對於每一對元素,我們將鍵和值放入結果HashMap
中,就像前面的示例一樣。
七、結論
在本文中,我們通過示例學習了三種將兩個給定列表組合成地圖的方法。
首先,我們用一個for
循環和基於列表隨機訪問的Stream
解決了這個問題。然後,我們討論了當輸入為LinkedList
時隨機訪問方法的性能問題。
最後,我們看到了基於Iterator
的解決方案,這樣無論我們有哪種List
實現,我們都可以獲得更好的性能。
與往常一樣,此處提供的所有代碼片段都可以在 GitHub 上找到。