簡體   English   中英

如何使用Java Streams API將層次結構映射圖轉換為平面映射圖

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM