![](/img/trans.png)
[英]Java 8 Streams: Make Collectors.groupingBy return Map<K, List<V>> instead of Map<K, List<List<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.