我想将流中的项收集到一个映射中,该映射将相等的对象分组在一起,并映射到出现的数量。
List<String> list = Arrays.asList("Hello", "Hello", "World");
Map<String, Long> wordToFrequency = // what goes here?
所以在这种情况下,我想地图由这些条目组成:
Hello -> 2
World -> 1
我该怎么做呢?
我想将流中的项收集到一个映射中,该映射将相等的对象分组在一起,并映射到出现的数量。
List<String> list = Arrays.asList("Hello", "Hello", "World");
Map<String, Long> wordToFrequency = // what goes here?
所以在这种情况下,我想地图由这些条目组成:
Hello -> 2
World -> 1
我该怎么做呢?
我认为你只是在寻找超载,这需要另一个Collector来指定对每个组做什么……然后collections .counting()来进行计数:
import java.util.*;
import java.util.stream.*;
class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Hello");
list.add("World");
Map<String, Long> counted = list.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(counted);
}
}
结果:
{Hello=2, World=1}
(也可以使用groupingByConcurrent来提高效率。如果在您的上下文中安全的话,那么在您的实际代码中需要记住这一点。)
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Hello");
list.add("World");
Map<String, List<String>> collect = list.stream()
.collect(Collectors.groupingBy(o -> o));
collect.entrySet()
.forEach(e -> System.out.println(e.getKey() + " - " + e.getValue().size()));
下面是StreamEx提供的简单解决方案:
StreamEx.of(list).groupingBy(Function.identity(), MoreCollectors.countingInt());
这样做的好处是减少了Java流样板代码:
下面是一个对象列表的例子
Map<String, Long> requirementCountMap = requirements.stream().collect(Collectors.groupingBy(Requirement::getRequirementType, Collectors.counting()));
这里有一些稍微不同的选项来完成手头的任务。
使用toMap:
list.stream()
.collect(Collectors.toMap(Function.identity(), e -> 1, Math::addExact));
使用地图::合并:
Map<String, Integer> accumulator = new HashMap<>();
list.forEach(s -> accumulator.merge(s, 1, Math::addExact));
如果您愿意使用第三方库,则可以使用Eclipse Collections中的Collectors2类使用流将List转换为Bag。Bag是为计数而构建的数据结构。
Bag<String> counted =
list.stream().collect(Collectors2.countBy(each -> each));
Assert.assertEquals(1, counted.occurrencesOf("World"));
Assert.assertEquals(2, counted.occurrencesOf("Hello"));
System.out.println(counted.toStringOfItemToCount());
输出:
{World=1, Hello=2}
在这种特殊情况下,您可以简单地将List直接收集到一个Bag中。
Bag<String> counted =
list.stream().collect(Collectors2.toBag());
通过使用Eclipse Collections协议调整List,您也可以在不使用流的情况下创建Bag。
Bag<String> counted = Lists.adapt(list).countBy(each -> each);
或者在这个特殊情况下:
Bag<String> counted = Lists.adapt(list).toBag();
您也可以直接创建Bag。
Bag<String> counted = Bags.mutable.with("Hello", "Hello", "World");
Bag<String>类似于Map<String, Integer>,因为它在内部跟踪键及其计数。但是,如果您向Map请求一个它不包含的键,它将返回null。如果您使用emergencesof向一个Bag请求它不包含的键,它将返回0。
注意:我是Eclipse Collections的提交者。