按Java 8流API分组


170

我尝试在Java 8流API中找到一种简单的方法来进行分组,我提出了这种复杂的方法!

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));
System.out.println(collect);

List<String[]> collect2 = collect
        .entrySet()
        .stream()
        .map(e -> new String[] { e.getKey(),
                String.valueOf(e.getValue().size()) })
        .collect(Collectors.toList());

collect2.forEach(o -> System.out.println(o[0] + " >> " + o[1]));

感谢您的投入。


1
您想在这里完成什么?
开普勒2014年

2
这是一个非常常见的情况,例如我在一个时间段内发生了错误,并且我想查看该时间段内每天发生次数的一些统计信息。
Muhammad Hewedy 2014年

Answers:


340

我认为您只是在寻找过载,它需要另一个负载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来提高效率。如果在您的上下文中安全的话,请记住您的真实代码。)


1
完善!...来自javadocand then performing a reduction operation on the values associated with a given key using the specified downstream Collector
Muhammad Hewedy 2014年

6
使用Function.identity()(通过静态导入)代替e-> e使其可读性更好:Map <String,Long> counted = list.stream()。collect(groupingBy(identity(),counting()) ));
库奇2015年

嗨,我想知道是否有人可以解释代码的Map方面,这时Map<String, Long> counted = list.stream() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));到底发生了什么以及与该主题相关的进一步说明的任何链接都可以发送出去
空白

@Blank:像感觉是最好的,因为一个新的问题,跟你解释这其中的一部分,你先了解。遍历它的各个方面(不知道您不了解哪一点)将花费很长的时间-比我愿意给出一个已经超过5年的答案的时间要多得多,而此时您大多数可能已经明白了。
乔恩·斯凯特

@JonSkeet酷,尽管我确实强调了我在问题中不了解的方面,但我将其添加到一个新问题中。就是说,我添加了整个代码片段。
空白

9
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()));

8

这是对象列表的示例

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

8

这是完成手头任务的略有不同的选择。

使用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));

4

这是StreamEx的简单解决方案

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

减少样板代码: collect(Collectors.


1
在Java8流上使用它的原因是什么?
Torsten Ojaperv,

1

如果您愿意使用第三方库,则可以使用 Collectors2的Eclipse收藏到转换ListBag使用Stream。A 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

Bag<String> counted = 
        list.stream().collect(Collectors2.toBag());

您还可以通过Bag使用Eclipse Collections协议Stream来修改而不使用来创建List

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");

A Bag<String>就像a Map<String, Integer>,它在内部跟踪键及其数量。但是,如果您要求一个Map不包含的密钥,它将返回null。如果您要求一个Bag不包含using的密钥occurrencesOf,它将返回0。

注意:我是Eclipse Collections的提交者。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.