如何使用 groupBy 计算出现次数?

问题描述 投票:0回答:7

我想将流中的项目收集到一个映射中,该映射将相等的对象分组在一起,并映射到出现的次数。

List<String> list = Arrays.asList("Hello", "Hello", "World");
Map<String, Long> wordToFrequency = // what goes here?

所以在这种情况下,我希望地图包含以下条目:

Hello -> 2
World -> 1

我该怎么做?

java functional-programming java-8
7个回答
513
投票

我认为你只是在寻找 overload ,它需要另一个

Collector
来指定如何处理每个组......然后
Collectors.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
来提高效率。如果在您的上下文中安全的话,您的实际代码需要记住一些事情。)


39
投票

这是对象列表的示例

Map<String, Long> requirementCountMap = requirements.stream().collect(Collectors.groupingBy(Requirement::getRequirementType, Collectors.counting()));

16
投票

这里有一些略有不同的选项来完成手头的任务。

使用

toMap

list.stream()
    .collect(Collectors.toMap(Function.identity(), e -> 1, Math::addExact));

使用

Map::merge

Map<String, Integer> accumulator = new HashMap<>();
list.forEach(s -> accumulator.merge(s, 1, Math::addExact));

13
投票
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()));

6
投票

这是 StreamEx 的简单解决方案:

StreamEx.of(list).groupingBy(Function.identity(), MoreCollectors.countingInt());

这具有减少 Java 流样板代码的优点:

collect(Collectors.


2
投票

如果您愿意使用第三方库,则可以使用

Eclipse Collections
中的 Collectors2 类,使用
List
Bag
转换为
Stream
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}

在这种特殊情况下,您可以简单地将

collect
直接转换为
List

Bag

您还可以通过使用 Eclipse Collections 协议调整 
Bag<String> counted = list.stream().collect(Collectors2.toBag());

,在不使用

Bag
的情况下创建
Stream

List

或者在这个特殊情况下:

Bag<String> counted = Lists.adapt(list).countBy(each -> each);

您也可以直接创建 Bag。

Bag<String> counted = Lists.adapt(list).toBag();

A 
Bag<String> counted = Bags.mutable.with("Hello", "Hello", "World");

就像

Bag<String>
一样,它在内部跟踪键及其计数。 但是,如果您向
Map<String, Integer>
询问它不包含的密钥,它将返回
Map
。 如果您使用
null
Bag
询问它不包含的密钥,它将返回 0。

注意:

我是 Eclipse Collections 的提交者。


0
投票
	
© www.soinside.com 2019 - 2024. All rights reserved.