在 Java 中將 Stream 轉換為 Map 或多圖
一、簡介
Java 8 發布後, Streams
成為 Java 不可或缺的一部分。它們是處理資料的有效、雄辯的方式。因此,有時可能必須將流的元素轉換為Map
或Multimap
。
在本教程中,我們將深入了解如何使用不同的方法和函式庫將流轉換為 Java 中的Map
或Multimap
。
2. Stream
到Map
的轉換
2.1.使用Collectors.toMap()
要將流轉換為Map,
我們可以使用Collectors.toMap()
函數。這樣的收集器指定鍵值映射函數,該函數會相應地映射流中的每個項目。這是一個基本範例:
@Test
public void givenStringStream_whenConvertingToMapWithMerge_thenExpectedMapIsGenerated() {
Stream<String> stringStream = Stream.of("one", "two", "three", "two");
Map<String, String> mergedMap = stringStream.collect(
Collectors.toMap(s -> s, s -> s, (s1, s2) -> s1 + ", " + s2)
);
// Define the expected map
Map<String, String> expectedMap = Map.of(
"one", "one",
"two", "two, two",
"three", "three"
);
assertEquals(expectedMap, mergedMap);
}
上面的測試方法首先建立一個字串Stream
stringStream,
使用Collectors.toMap()
將其放入Map
中。此函數將每個字串作為其鍵和值,以逗號分隔,以合併同一鍵的多個條目。
2.2.使用Stream.reduce()
我們也可以使用Stream.reduce()
運算子。這個方法可以幫助我們使用恆等函數和累加函數將流的值建構到Map
中。
@Test
public void givenStringStream_whenConvertingToMapWithStreamReduce_thenExpectedMapIsGenerated() {
Stream<String> stringStream = Stream.of("one", "two", "three", "two");
Map<String, String> resultMap = stringStream.reduce(
new HashMap<>(), (map, element) -> {
map.put(element, element);
return map;
},
(map1, map2) -> {
map1.putAll(map2);
return map1;
}
);
Map<String, String> expectedMap = new HashMap<>();
expectedMap.put("one", "one");
expectedMap.put("two", "two");
expectedMap.put("three", "three");
assertEquals(expectedMap, resultMap);
}
請注意, Stream.reduce()
運算子遇到相同鍵的重複值,它以與上一節不同的方式累積它們,而不是用最後遇到的值覆蓋現有值,而是透過為該鍵建立值列表來聚合它們。
這就是為什麼「 two
」被映射到結果映射中包含兩個「 two
」值的列表,而在2.1節中,它用逗號將它們連接起來。
3. Stream
到Multimap
的轉換
3.1.使用Guava's Multimap
Google 的 Guava 庫中有一個Multimap
接口,它將一個特定的鍵映射到多個值。首先,我們需要將其作為依賴項包含在我們的專案中:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
</dependency>
然後,我們利用它將流轉換為ListMultimap
,如下所示:
@Test
public void givenStringStream_whenConvertingToMultimap_thenExpectedMultimapIsGenerated() {
Stream<String> stringStream = Stream.of("one", "two", "three", "two");
ListMultimap<String, String> multimap = stringStream.collect(
ArrayListMultimap::create,
(map, element) -> map.put(element, element),
ArrayListMultimap::putAll
);
ListMultimap<String, String> expectedMultimap = ArrayListMultimap.create();
expectedMultimap.put("one", "one");
expectedMultimap.put("two", "two");
expectedMultimap.put("two", "two");
expectedMultimap.put("three", "three");
assertEquals(expectedMultimap, multimap);
}
在上面的程式碼中,我們利用ArrayListMultimap::create
方法來收集stringStream
元素。此外, (map, element) -> map.put(element, element)
迭代流元素以將每個元素放入multimap
。這可確保多重映射中的鍵和值相同,從而保留重複的條目。第三個函數ArrayListMultimap::putAll
可以根據需要將多個多重映射結果合併為一個。
3.2.使用Stream.reduce()
將流轉換為Multimap
另一種方法是對流應用reduce
作業。反過來,這使我們能夠透過身分值和累積函數來完成轉換任務:
@Test
public void givenStringStream_whenConvertingToMultimapWithStreamReduce_thenExpectedMultimapIsGenerated() {
Stream<String> stringStream = Stream.of("one", "two", "three", "two");
Map<String, List<String>> multimap = stringStream.reduce(
new HashMap<>(),
(map, element) -> {
map.computeIfAbsent(element, k -> new ArrayList<>()).add(element);
return map;
},
(map1, map2) -> {
map2.forEach((key, value) -> map1.merge(key, value, (list1, list2) -> {
list1.addAll(list2);
return list1;
}));
return map1;
}
);
Map<String, List<String>> expectedMultimap = new HashMap<>();
expectedMultimap.put("one", Collections.singletonList("one"));
expectedMultimap.put("two", Arrays.asList("two", "two"));
expectedMultimap.put("three", Collections.singletonList("three"));
assertEquals(expectedMultimap, multimap);
}
在這裡,透過reduce 操作,測試方法將stringStream
的元素累積到multimap
中,其中每個唯一字串都對應到其出現的清單。我們也使用 lambda 表達式來處理值的對應和合併,然後透過斷言來確保轉換的正確性。
三、結論
總之,Java 流提供了高效的資料處理,本教學介紹了Collectors.toMap()
、 Stream.reduce()
和Guava's Multimap
等方法,用於將流轉換為Map
和Multimap
。這些方法使我們能夠在 Java 中有效地處理數據,從而靈活地選擇適合專案需求的正確方法。
與往常一樣,本文的完整程式碼範例可以在 GitHub 上找到。