Java8中Collectors详解



前言

流可以用类似于数据库的操作帮助你处理集合。你可以把Java 8的流看作花哨又懒惰的数据集迭代器。它们支持两种类型的操作:中间操作(如filter或map)和终端操作(如count、findFirst、forEach和reduce)。中间操作可以链接起来,将一个流转换为另一个流。这些操作不会消耗流,其目的是建立一个流水线。与此相反,终端操作会消耗流,以产生一个最终结果,例如返回流中的最大元素。它们通常可以通过优化流水线来缩短计算时间。


基础数据准备

   List<PersonDto> personDtos = ListUtil.toList(
                new PersonDto(1, "张三", 174, 32),
                new PersonDto(4, "李四", 173, 23),
                new PersonDto(4, "赵六", 185, 20),
                new PersonDto(3, "王五", 192, 20),
                new PersonDto(5, "孙七", 169, 26)
        );

1. averagingDouble

averagingDouble方法返回一个Collector收集器,它生成应用于输入元素的double值函数的算术平均值。如果没有元素,则结果为0。

Double collect = personDtos.stream().collect(Collectors.averagingInt(PersonDto::getAge));
Optional.ofNullable(collect).ifPresent(System.out::println);//24.2

Double collect1 = personDtos.stream().collect(Collectors.averagingDouble(PersonDto::getAge));
Optional.ofNullable(collect1).ifPresent(System.out::println);//24.2

Double collect2 = personDtos.stream().collect(Collectors.averagingLong(PersonDto::getAge));
Optional.ofNullable(collect2).ifPresent(System.out::println);//24.2

2. collectingAndThen

该方法先执行了一个归纳操作,然后再对归纳的结果进行 Function 函数处理输出一个新的结果。

//(1)
Optional.ofNullable(personDtos.stream().collect(
		//第一个参数为要做的操作,第二个参数对第一个参数收集到的结果进行如何的处理
       Collectors.collectingAndThen(
               Collectors.averagingInt(PersonDto::getAge), a -> "The Average age is->" + a)
).ifPresent(System.out::println);//The Average age is->24.2

//(2)
Integer collect4 = personDtos.stream().collect(Collectors.collectingAndThen(Collectors.groupingBy(x -> x.getAge()), map -> map.size()));
 System.out.println(collect4);//4

3. counting

counting方法返回一个Collector收集器接受T类型的元素,用于计算输入元素的数量。如果没有元素,则结果为0。

Optional.of(personDtos.stream().collect(Collectors.counting())).ifPresent(System.out::println);
// 5

4. groupingBy

4.1 groupingBy(Function)

groupingBy(Function)方法返回一个Collector收集器对T类型的输入元素执行"group by"操作,根据分类函数对元素进行分组,并将结果返回到Map。

分类函数将元素映射到某些键类型K。收集器生成一个Map>,其键是将分类函数应用于输入元素得到的值,其对应值为List,其中包含映射到分类函数下关联键的输入元素。
无法保证返回的Map或List对象的类型,可变性,可序列化或线程安全性。
注意: 返回的Collector收集器不是并发的。对于并行流管道,combiner函数通过将键从一个映射合并到另一个映射来操作,这可能是一个昂贵的操作。如果不需要保留元素出现在生成的Map收集器中的顺序,则使用groupingByConcurrent(Function)可以提供更好的并行性能。

//查询人员信息
 Map<String, List<PersonDto>> collect = personDtos.stream().collect(Collectors.groupingBy(PersonDto::getName));
Optional.ofNullable(collect).ifPresent(System.out::println);

//{孙七=[PersonDto{id=5, name='孙七', tall=169, age=26}], 李四=[PersonDto{id=4, name='李四', tall=173, age=23}], 张三=[PersonDto{id=1, name='张三', tall=174, age=32}], 王五=[PersonDto{id=3, name='王五', tall=192, age=20}], 赵六=[PersonDto{id=4, name='赵六', tall=185, age=20}]}

4.2 groupingBy(Function, Collector)

groupingBy(Function, Collector)方法返回一个Collector收集器,对T类型的输入元素执行级联"group by"操作,根据分类函数对元素进行分组,然后使用指定的下游Collector收集器对与给定键关联的值执行缩减操作。

分类函数将元素映射到某些键类型K。下游收集器对T类型的元素进行操作,并生成D类型的结果。产生收集器生成Map<K, D>。
返回的Map的类型,可变性,可序列化或线程安全性无法保证。
注意: 返回的Collector收集器不是并发的。对于并行流管道,combiner函数通过将键从一个映射合并到另一个映射来操作,这可能是一个昂贵的操作。如果不需要保留向下游收集器提供元素的顺序,则使用groupingByConcurrent(Function,
Collector)可以提供更好的并行性能。

//查询人员个数
Map<String, Long> collect1 = personDtos.stream().collect(Collectors.groupingBy(PersonDto::getName, Collectors.counting()));
System.out.println("collect1 = " + collect1);

//collect1 = {孙七=1, 李四=1, 张三=1, 王五=2, 赵六=1}

4.3 groupingBy(Function, Supplier, Collector)

groupingBy(Function, Supplier, Collector)方法返回一个Collector收集器,对T类型的输入元素执行级联"group by"操作,根据分类函数对元素进行分组,然后使用指定的下游Collector收集器对与给定键关联的值执行缩减操作。收集器生成的Map是使用提供的工厂函数创建的。

分类函数将元素映射到某些键类型K。下游收集器对T类型的元素进行操作,并生成D类型的结果。产生收集器生成Map<K, D>。
注意: 返回的Collector收集器不是并发的。对于并行流管道,combiner函数通过将键从一个映射合并到另一个映射来操作,这可能是一个昂贵的操作。如果不需要保留向下游收集器提供元素的顺序,则使用groupingByConcurrent(Function,
Supplier, Collector)可以提供更好的并行性能。

//按照年龄分组,查询各个年龄的总人数,并按升序排列

TreeMap<Integer, Long> collect = personDtos.stream().collect(Collectors.groupingBy(PersonDto::getAge, TreeMap::new, Collectors.counting()));
System.out.println("collect = " + collect);

//这里的Supplier使用的是TreeMap,TreeMap是有序的。
//collect = {20=2, 23=1, 26=1, 32=1}

5. joining() & joining(delimiter) & joining(delimiter, prefix, suffix)

joining()方法返回一个Collector收集器,它按遇见顺序将输入元素连接成String。
joining(delimiter)方法返回一个Collector收集器,它以遇见顺序连接由指定分隔符分隔的输入元素。
joining(delimiter, prefix, suffix)方法返回一个Collector收集器,它以遇见顺序将由指定分隔符分隔的输入元素与指定的前缀和后缀连接起来。

//1  joining()
String collect = personDtos.stream().map(PersonDto::getName).collect(Collectors.joining());
System.out.println("collect = " + collect);
//collect = 张三李四赵六王五孙七

//2 joining(delimiter)
String collect1 = personDtos.stream().map(PersonDto::getName).collect(Collectors.joining(","));
System.out.println("collect1 = " + collect1);
//collect1 = 张三,李四,赵六,王五,孙七

//3 joining(delimiter, prefix, suffix)
String collect2 = personDtos.stream().map(PersonDto::getName).collect(Collectors.joining(",","[","]"));
System.out.println("collect2 = " + collect2);
//collect2 = [张三,李四,赵六,王五,孙七]

6. mapping

mapping方法通过在累积之前将映射函数应用于每个输入元素,将Collector收集器接受U类型的元素调整为一个接受T类型的元素。

注意:map mapping 以下两种写法
personDtos.stream().collect(Collectors.mapping(PersonDto::getName, Collectors.joining(“;”)));
personDtos.stream().map(PersonDto::getName).collect(Collectors.joining(“;”))

String collect4 = personDtos.stream().collect(Collectors.mapping(PersonDto::getName, Collectors.joining(";")));
System.out.println("collect4 = " + collect4);

String collect3 = personDtos.stream().map(PersonDto::getName).collect(Collectors.joining(";"));
System.out.println("collect3 = " + collect3);

//collect3 = 张三;李四;赵六;王五;孙七
//collect4 = 张三;李四;赵六;王五;孙七

7. maxBy minBy

maxBy方法返回一个Collector收集器,它根据给定的Comparator比较器生成最大元素,描述为Optional。
minBy方法返回一个Collector收集器,它根据给定的Comparator比较器生成最小元素,描述为Optional。

//身高最高
personDtos.stream().collect(Collectors.maxBy(Comparator.comparingInt(PersonDto::getTall))).ifPresent(System.out::println);
//PersonDto{id=3, name='王五', tall=192, age=20}

//身高最低
personDtos.stream().collect(Collectors.minBy(Comparator.comparingInt(PersonDto::getTall))).ifPresent(System.out::println);
//PersonDto{id=5, name='孙七', tall=169, age=26}

8. partitioningBy(Predicate) & partitioningBy(Predicate, Collector)

partitioningBy(Predicate)方法返回一个Collector收集器,它根据Predicate对输入元素进行分区,并将它们组织成Map。
partitioningBy(Predicate, Collector)方法返回一个Collector收集器,它根据Predicate对输入元素进行分区,根据另一个Collector收集器减少每个分区中的值,并将它们组织成Map,其值是下游减少的结果。

注意:返回的Map的类型,可变性,可序列化或线程安全性无法保证


9. reducing(BinaryOperator) & reducing(Object, BinaryOperator) & reducing(Object, Function, BinaryOperator)

  • reducing()相关收集器在groupingBy或partitioningBy下游的多级缩减中使用时非常有用。要对流执行简单缩减。

9.1 reducing(BinaryOperator)

返回一个Collector收集器,它在指定的BinaryOperator下执行其输入元素的缩减。结果被描述为Optional。

//查询年龄最大的人
//方法(1)
Optional<PersonDto> collect = personDtos
        .stream()
        .collect(Collectors
                .reducing(BinaryOperator
                        .maxBy(Comparator
                                .comparingInt(PersonDto::getAge))));
System.out.println("collect = " + collect);

//collect = Optional[PersonDto{id=1, name='张三', tall=174, age=32}]

//方法(2)
Optional<PersonDto> reduce = personDtos
        .stream()
        .reduce(BinaryOperator
                .maxBy(Comparator
                        .comparingInt(PersonDto::getAge)));
System.out.println("reduce = " + reduce);

//reduce = Optional[PersonDto{id=1, name='张三', tall=174, age=32}]

9.2 reduce(Object, BinaryOperator)

返回一个Collector收集器,它使用提供的标识在指定的BinaryOperator下执行其输入元素的缩减。

//查询年龄总和

//方法(1)
Integer collect = personDtos
        .stream()
        .map(PersonDto::getAge)
        .collect(Collectors.reducing(0, (d1, d2) -> d1 + d2));
System.out.println("collect = " + collect);

//collect = 121

//方法(2)
Integer reduce = personDtos
        .stream()
        .map(PersonDto::getAge)
        .reduce(0, (d1, d2) -> d1 + d2);
System.out.println("reduce = " + reduce);

//reduce = 121

9.3 reducing(Object, Function, BinaryOperator)

返回一个Collector收集器,它在指定的映射函数和BinaryOperator下执行其输入元素的缩减。这是对reducing(Object, BinaryOperator)的概括,它允许在缩减之前转换元素。

//查询年龄总和

//方法(1)
Integer collect = personDtos
        .stream()
        .collect(Collectors.reducing(0, PersonDto::getAge, (d1, d2) -> d1 + d2));
System.out.println("collect = " + collect);

//collect = 121

//方法(2)
Integer reduce = personDtos
        .stream()
        .map(PersonDto::getAge)
        .reduce(0, (d1, d2) -> d1 + d2);
System.out.println("reduce = " + reduce);

//reduce = 121
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值