java - 地图返回地图> groupingBy value 之后,而不是 Map>>
问题描述
我正在努力维护我想要跨 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>>
.
所以具体的问题是,如何转换和包含流操作产生的数据结构输出?
我已经看到 Collectors 提供了对 Map(...) 的转换操作,我想这是要走的路,但我无法(由于缺乏适当的知识,我认为)让它工作。
在这种情况下,在我看来,我将通过教学解释、链接到综合资源以更好地理解流和函数式编程或类似的东西,而不是特定案例的实际解决方案(这对一个练习,但你明白了)
解决方案
您在这里遇到困难有点令人惊讶,因为您已经展示了所有必要事物的知识。您知道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
将它们附加到末尾,保持插入顺序。
推荐阅读
- javascript - 如何使用 require 而不是 import 加载 javascript 库
- reactjs - 将数据传递给 React-router 中的其他组件
- android - 创建导航资源文件时的IDE错误
- django - 我应该如何正确地为模型添加权限?
- python - 如何用 pandas 读取 Spark 编写的文件?
- arrays - 在 Matlab 中将矩阵元素转换为字符
- cadence-workflow - 如何使用 Uber Cadence 设计一个无状态工作者只处理一次消息
- php - 如何随机应用一个类而不重复循环?
- css - SVG 元素的不透明度与可见性属性和特异性
- javascript - 导航栏位于屏幕顶部时的 Jquery 变得粘滞不工作