[英]how to use java streams api to transform a hierarchal map to a flat map
因此,我有一個使用映射的函數,其中一個名稱與數組中多個不同索引之一相關聯。 索引號將永遠只有一個與之關聯的名稱,因此沒有重復且沒有null,因此可以安全地使用以下函數來平整層次結構。
public Map<Integer, Object> normalize( Map<Object, List<Integer>> hierarchalMap ) {
Map<Integer, Object> normalizedMap = new HashMap<>();
for (Map.Entry<Object, List<Integer>> entry : hierarchalMap.entrySet()) {
for (Integer integer : entry.getValue()) {
noramizedMap.put(integer, entry.getKey());
}
}
return normalizedMap;
}
我正在嘗試將此功能更改為使用流API,到目前為止,我已經做到了:
Map<Integer, Object> noramizedMap = new HashMap<>();
for (Map.Entry<Object, List<Integer>> entry : vars.entrySet()) {
entry.getValue().forEach(e -> noramizedMap.put(e, entry.getValue()));
}
如果這是其他某種功能性語言,我會做部分綁定或其他操作,但是使用java時,我嘗試將外循環展開為流... collectTo我只是迷路了。
假設我的評論正確,則可以使用以下Streams:
hierarchalMap.entrySet()
.stream()
.flatMap(entry -> entry.getValue()
.stream()
.map(i -> new AbstractMap.SimpleEntry<>(entry.getKey(), i)))
.collect(Collectors.toMap(Entry::getValue, Entry::getKey));
假定沒有重復且沒有null。
我認為這應該可以滿足您的需求:
public Map<Integer, Object> normalizeJava8(Map<Object, List<Integer>> hierarchalMap ) {
return hierarchalMap
.entrySet()
.stream()
.collect(
HashMap::new,
(map, entry) ->
entry.getValue().forEach(i -> map.put(i, entry.getKey())),
HashMap::putAll);
}
通常,在使用Java 8流時,由於缺少便捷的元組類型,與其他語言的等效構造相比,您必須在操作的“收集”部分中放置更多的邏輯。 直觀地講,創建成對的列表然后將它們收集到一個映射中似乎更為理智,但是與將邏輯放入.collect
相比,最終導致更多的代碼和更高的計算強度。
使用流和Java 9:
Map<Integer, Object> normalizedMap = hierarchalMap.entrySet().stream()
.flatMap(e -> e.getValue().stream().map(i -> Map.entry(i, e.getKey())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
這與該答案幾乎相同,除了我使用Map.entry()
方法創建對,並將整數作為鍵。
這是不使用流的另一種不太冗長的方法:
Map<Integer, Object> normalizedMap = new HashMap<>();
hierarchalMap.forEach((k, v) -> v.forEach(i -> normalizedMap.put(i, k)));
您可以在Java 8中使用以下兩個便利收集器,它們不僅限於地圖。
public static <T, K, V> Collector<T, ?, Map<K, V>> flatInverseMapping(Function<? super T, ? extends Stream<? extends K>> keyStreamFunction,
Function<? super T, ? extends V> valueFunction) {
return Collector.of(HashMap::new,
(m, v) ->
keyStreamFunction.apply(v).forEach(innerV -> m.put(innerV, valueFunction.apply(v))),
(m1, m2) -> {
m1.putAll(m2);
return m2;
});
}
public static <T, K, V> Collector<T, ?, Map<K, V>> flatInverseMapping(Function<? super T, ? extends Collection<? extends K>> keyStreamFunction,
Function<? super T, ? extends V> valueFunction) {
return Collector.of(HashMap::new,
(m, v) ->
keyStreamFunction.apply(v).forEach(innerV -> m.put(innerV, valueFunction.apply(v))),
(m1, m2) -> {
m1.putAll(m2);
return m2;
});
}
由於流和集合都具有forEach,因此除了輸入對象外,這兩種實現都相同。
為了簡要說明這是如何工作的,收集器的輸出是由兩個函數的輸出定義的K和V(鍵和值)參數的映射。 對於流中從輸入對象派生的每個鍵值,將應用相同的值函數,以便在共享鍵之間使用一致的值反轉映射。 請注意,如果流中有多個項目可以解析為相同的鍵,則不會像正常的toMap實現那樣拋出合並異常。 BiConsumer將需要更改為此,以保持該行為:
(var1, var2) -> {
Iterator<Map.Entry<K,V>> var3 = var2.entrySet().iterator();
while(var3.hasNext()) {
Map.Entry<K,V> var4 = var3.next();
var1.merge(var4.getKey(), var4.getValue(), (v0, v1) -> {
throw new IllegalStateException(String.format("Duplicate key %s", v0));
});
}
return var1;
}
作為參考,它實際上是從Collectors.toMap代碼復制的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.