使用 Java 流 API 基于每个项目频率作为键将值分组在一起
Using the Java Stream API to group values together based upon each item frequency as the key
我接触过使用 Java 流 API 通过 groupingBy
和 counting
根据项目在集合中出现的频率对项目进行分组的模式。例如,
Map<String,Long> counts = Arrays.stream(words)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
我想知道是否有一种简单的方法来执行此操作的逆操作。更明确地定义,我想对项目进行分组,键是它们出现的频率,值是值的集合。基本上这段代码的作用:
Map<String,Long> counts = Arrays.stream(words)
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting())
);
Map<Long,List<String>> map = new HashMap<>();
for(Map.Entry<String,Long> entry : counts.entrySet())
map.computeIfAbsent(
entry.getValue(),
i -> new ArrayList<>()
).add(entry.getKey());
所以单词的示例输入可以是
["lorem","ipsum","lorem","lorem","dolor","dolor","sit"]
产生
的输出
{1:["ipsum","sit"],2:["dolor"],3:["lorem"]}
到目前为止,我能够使用 Stream API 最接近的是这个怪物(必须有更好的方法)
Map<Long,List<String>> map =
Arrays.stream(words)
.collect(
Collectors.collectingAndThen(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
),
stringLongMap -> stringLongMap.entrySet().stream()
.collect(
Collectors.collectingAndThen(
Collectors.groupingBy(entry -> entry.getValue()),
longEntryMap -> longEntryMap.entrySet()
.stream()
.collect(
Collectors.toMap(Map.Entry::getKey,
e -> e.getValue().stream()
.map(i -> i.getKey())
.collect(Collectors.toList())))))));
以上方式超级迂回,不切实际,不可读,其他方面很糟糕。我什至觉得想出来很恶心。我希望有一种方法可以做到这一点,类似于收集器 API 页面
中的示例
// Group employees by department
Map<Department, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
当我将 Collectors.counting()
放在 groupingBy
中时,编译器会感到不安。最终,这就是我希望分组的那个。有没有更优雅的流方式来获得 Map<Long,List<String>>
,其中键对应于频率,值对应于具有该频率的所有项目的集合?
谢谢。
最简单的方法是对单词进行频率计数,然后流式传输该映射的条目并反转键和值。
String[] arr = { "lorem", "ipsum", "lorem", "lorem", "dolor",
"dolor", "sit" };
Map<Long, List<String>> freq = Arrays.stream(arr).collect(Collectors
.groupingBy(str -> str, Collectors.counting())).entrySet()
.stream()
.collect(Collectors.groupingBy(Entry::getValue,
Collectors.mapping(Entry::getKey,
Collectors.toList())));
freq.entrySet().forEach(System.out::println);
打印
1=[ipsum, sit]
2=[dolor]
3=[lorem]
如果您要获取之前的计数地图,则只需执行此操作。
Map<Long, List<String>> result = counts.entrySet().stream()
.collect(Collectors.groupingBy(Entry::getValue,
Collectors.mapping(Entry::getKey,
Collectors.toList())));
我接触过使用 Java 流 API 通过 groupingBy
和 counting
根据项目在集合中出现的频率对项目进行分组的模式。例如,
Map<String,Long> counts = Arrays.stream(words)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
我想知道是否有一种简单的方法来执行此操作的逆操作。更明确地定义,我想对项目进行分组,键是它们出现的频率,值是值的集合。基本上这段代码的作用:
Map<String,Long> counts = Arrays.stream(words)
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting())
);
Map<Long,List<String>> map = new HashMap<>();
for(Map.Entry<String,Long> entry : counts.entrySet())
map.computeIfAbsent(
entry.getValue(),
i -> new ArrayList<>()
).add(entry.getKey());
所以单词的示例输入可以是
["lorem","ipsum","lorem","lorem","dolor","dolor","sit"]
产生
的输出 {1:["ipsum","sit"],2:["dolor"],3:["lorem"]}
到目前为止,我能够使用 Stream API 最接近的是这个怪物(必须有更好的方法)
Map<Long,List<String>> map =
Arrays.stream(words)
.collect(
Collectors.collectingAndThen(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
),
stringLongMap -> stringLongMap.entrySet().stream()
.collect(
Collectors.collectingAndThen(
Collectors.groupingBy(entry -> entry.getValue()),
longEntryMap -> longEntryMap.entrySet()
.stream()
.collect(
Collectors.toMap(Map.Entry::getKey,
e -> e.getValue().stream()
.map(i -> i.getKey())
.collect(Collectors.toList())))))));
以上方式超级迂回,不切实际,不可读,其他方面很糟糕。我什至觉得想出来很恶心。我希望有一种方法可以做到这一点,类似于收集器 API 页面
中的示例// Group employees by department
Map<Department, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
当我将 Collectors.counting()
放在 groupingBy
中时,编译器会感到不安。最终,这就是我希望分组的那个。有没有更优雅的流方式来获得 Map<Long,List<String>>
,其中键对应于频率,值对应于具有该频率的所有项目的集合?
谢谢。
最简单的方法是对单词进行频率计数,然后流式传输该映射的条目并反转键和值。
String[] arr = { "lorem", "ipsum", "lorem", "lorem", "dolor",
"dolor", "sit" };
Map<Long, List<String>> freq = Arrays.stream(arr).collect(Collectors
.groupingBy(str -> str, Collectors.counting())).entrySet()
.stream()
.collect(Collectors.groupingBy(Entry::getValue,
Collectors.mapping(Entry::getKey,
Collectors.toList())));
freq.entrySet().forEach(System.out::println);
打印
1=[ipsum, sit]
2=[dolor]
3=[lorem]
如果您要获取之前的计数地图,则只需执行此操作。
Map<Long, List<String>> result = counts.entrySet().stream()
.collect(Collectors.groupingBy(Entry::getValue,
Collectors.mapping(Entry::getKey,
Collectors.toList())));