簡體   English   中英

在分組值之后將<K,V>映射回地圖<V,映射<K,V >>,而不是Map <Obj,List <Entry <K,V >>>

[英]Map<K,V> back to Map<V,Map<K,V>> after groupingBy value, instead of Map<Obj, List<Entry<K,V>>>

我正在努力維護我希望在Java中進行流操作的數據結構,這很可能是由於缺乏適當的理解和實踐。

public class Main {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 1, 1, 2, 3, 3, 3, 3);

            //Group by
            Map <Integer, Long> countGrouped = list.stream().collect(
                    Collectors.groupingBy(
                            x -> x, Collectors.counting()));
            System.out.println("group by value, count " + countGrouped);

            //Sort desc
            Map <Integer, Long> descendingSorted = new LinkedHashMap<>();
            countGrouped.entrySet().stream()
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                .forEachOrdered(x -> descendingSorted.put(x.getKey(), x.getValue()));
            System.out.println("sorted " + descendingSorted);

            //filter
            Map <Integer, Long> filtered = new LinkedHashMap<>();
            descendingSorted.entrySet().stream()
                .filter(x -> x.getValue() >= 2)
                .forEach(x -> filtered.put(x.getKey(), x.getValue()));;
            System.out.println("filtered " + filtered);

            //Split groups
            Map<Object, List<Entry<Integer, Long>>> groups = filtered.entrySet().stream()
                    .collect(Collectors.groupingBy(x -> x.getValue()));
            System.out.println("grouped " + groups);
    }
}

導致

group by value, count {1=3, 2=1, 3=4}
sorted {3=4, 1=3, 2=1}
filtered {3=4, 1=3}
grouped {3=[1=3], 4=[3=4]}

這是正確的,但我正逐漸進入更加深奧的數據結構而沒有特別的意義,正如你所看到的那樣,你可以看到,在(wtf?) Map<Object, List<Entry<Integer, Long>>>中完成。 雖然它可以只是Map<Int, Map<Int, Int>>

所以具體問題是,如何轉換和包含流操作產生的數據結構輸出?

我已經看到收集器提供轉換操作到地圖(...),我想這是要走的路,但我不能(由於缺乏適當的知識,我認為)讓它工作。

在這種情況下,在我看來,我將通過一個教學解釋大大幫助,鏈接到全面的資源,以更好地理解流和函數式編程,或類似的東西,而不是特定情況的實際解決方案(這將是很好的練習,但你明白了)

你在這里遇到困難有點令人驚訝,因為你已經展示了所有必要事物的知識。 你知道groupingBy可以將另一個Collector命名為toMap ,你已經使用了函數來提取Map.Entry值。

結合這些東西,給你

Map<Long, Map<Integer, Long>> groups = filtered.entrySet().stream()
    .collect(Collectors.groupingBy(x -> x.getValue(),
        Collectors.toMap(x -> x.getKey(), x -> x.getValue())));
System.out.println("grouped " + groups);

為了更好地演示操作,我將輸入更改為

List<Integer> list = Arrays.asList(1, 1, 1, 2, 3, 3, 3, 3, 4, 4, 4);

結果

grouped {3=[1=3, 4=3], 4=[3=4]}

但是,重復計數總是與外部地圖鍵相同沒有意義。 所以另一種選擇

Map<Long, List<Integer>> groups = filtered.entrySet().stream()
    .collect(Collectors.groupingBy(Map.Entry::getValue,
        Collectors.mapping(Map.Entry::getKey, Collectors.toList())));
System.out.println("grouped " + groups);

這導致

grouped {3=[1, 4], 4=[3]}

請注意,您不應該使用forEach / forEachOrdered put地圖。 你的中間步驟應該是

//Sort desc
Map<Integer, Long> descendingSorted = countGrouped.entrySet().stream()
    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
        (a,b) -> { throw new AssertionError(); }, LinkedHashMap::new));
System.out.println("sorted " + descendingSorted);

//filter
Map<Integer, Long> filtered = descendingSorted.entrySet().stream()
    .filter(x -> x.getValue() >= 2)
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
        (a,b) -> { throw new AssertionError(); }, LinkedHashMap::new));
System.out.println("filtered " + filtered);

接受地圖工廠的toMap收集器迫使我們提供合並功能,但由於我們的輸入已經是一個必須具有不同鍵的地圖,所以我在這里提供了一個總是拋出的功能,因為如果出現重復,則會出現嚴重錯誤。

但請注意,強制所有這些操作收集到新地圖中是不必要的復雜和低效。 首先對整個數據進行排序並通過filter減少數據量也沒有意義。 首先過濾可能會減少排序步驟的工作,而過濾操作的結果不應該依賴於順序。

在單個管道中執行整個操作要好得多

List<Integer> list = Arrays.asList(1, 1, 1, 2, 3, 3, 3, 3, 4, 4, 4);

Map<Integer, Long> countGrouped = list.stream().collect(
    Collectors.groupingBy(x -> x, Collectors.counting()));
System.out.println("group by value, count " + countGrouped);

Map<Long, List<Integer>> groups = countGrouped.entrySet().stream()
    .filter(x -> x.getValue() >= 2)
    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
    .collect(Collectors.groupingBy(Map.Entry::getValue, LinkedHashMap::new, 
        Collectors.mapping(Map.Entry::getKey, Collectors.toList())));

System.out.println("grouped " + groups);

請注意,與前面的代碼不同,現在最后的分組操作也將保留順序,從而導致

grouped {4=[3], 3=[1, 4]}

即,組按降序計數。

由於計數是結果映射的關鍵,我們還可以使用內部排序的映射作為結果類型,並省略排序步驟:

Map<Long, List<Integer>> groups = countGrouped.entrySet().stream()
    .filter(x -> x.getValue() >= 2)
    .collect(Collectors.groupingBy(Map.Entry::getValue,
        () -> new TreeMap<>(Comparator.<Long>reverseOrder()),
        Collectors.mapping(Map.Entry::getKey, Collectors.toList())));

主要區別在於流操作結果映射的行為,例如,如果向其插入更多元素,因為TreeMap將按降序插入新鍵,而LinkedHashMap會將它們追加到末尾,從而保持插入順序。

groupingBy的簽名是public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier) ,但如果我理解正確你想要只是將值映射到地圖條目,如:

Map<Object, Map.Entry<Integer, Long>> groups = filtered.entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getValue, x -> x));
System.out.println("grouped " + groups);

產量

grouped {3=1=3, 4=3=4}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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