Java8中Collectors详解

https://blog.csdn.net/sl1992/article/details/98900343
java.util.stream.Collectors实现各种有用的缩减操作的Collector的实现,例如将元素累积到集合中,根据各种标准汇总元素等。

以下以示例操作展示Collectors的详细用法,多数的操作是可以嵌套组合的,根据实际需要得到相应的结果。

  1. /**
  2. * 学生信息
  3. */
  4. public class Student {
  5. /** 姓名 */
  6. private String name;
  7. /** 总分 */
  8. private int totalScore;
  9. /** 是否本地人 */
  10. private boolean local;
  11. /** 年级 */
  12. private GradeType gradeType;
  13. /**
  14. * 年级类型
  15. */
  16. public enum GradeType {ONE,TWO,THREE}
  17. public Student(String name, int totalScore, boolean local, GradeType gradeType) {
  18. this.name = name;
  19. this.totalScore = totalScore;
  20. this.local = local;
  21. this.gradeType = gradeType;
  22. }
  23. @Override
  24. public String toString() {
  25. return "Student{" +
  26. "name='" + name + '\'' +
  27. ", totalScore=" + totalScore +
  28. ", local=" + local +
  29. ", gradeType=" + gradeType +
  30. '}';
  31. }
  32. public String getName() {
  33. return name;
  34. }
  35. public void setName(String name) {
  36. this.name = name;
  37. }
  38. public int getTotalScore() {
  39. return totalScore;
  40. }
  41. public void setTotalScore(int totalScore) {
  42. this.totalScore = totalScore;
  43. }
  44. public boolean isLocal() {
  45. return local;
  46. }
  47. public void setLocal(boolean local) {
  48. this.local = local;
  49. }
  50. public GradeType getGradeType() {
  51. return gradeType;
  52. }
  53. public void setGradeType(GradeType gradeType) {
  54. this.gradeType = gradeType;
  55. }
  56. }

初始化测试数据

List<Student> menu = Arrays.asList(
    new Student("刘一", 721, true, Student.GradeType.THREE),
    new Student("陈二", 637, true, Student.GradeType.THREE),
    new Student("张三", 666, true, Student.GradeType.THREE),
    new Student("李四", 531, true, Student.GradeType.TWO),
    new Student("王五", 483, false, Student.GradeType.THREE),
    new Student("赵六", 367, true, Student.GradeType.THREE),
    new Student("孙七", 499, false, Student.GradeType.ONE));

1.averagingDouble 平均值

序号 修饰符和类型 方法和描述
1 static <T> Collector<T,?,Double> averagingDouble(ToDoubleFunction<? super T> mapper)
返回一个收集器,它产生应用于输入元素的双值函数的算术平均值
2 static <T> Collector<T,?,Double> averagingInt(ToIntFunction<? super T> mapper)
返回一个收集器,它产生应用于输入元素的整数值函数的算术平均值
3 static <T> Collector<T,?,Double> averagingLong(ToLongFunction<? super T> mapper)
返回一个收集器,它产生应用于输入元素的长值函数的算术平均值

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

返回的平均值可能会因记录值的顺序而变化,这是由于除了不同大小的值之外,还存在累积舍入误差。通过增加绝对量排序的值(即总量,样本越大,结果越准确)往往会产生更准确的结果。如果任何记录的值是NaN或者总和在任何点NaN,那么平均值将是NaN。

注意: double格式可以表示-253到253范围内的所有连续整数。如果管道有超过253的值,则平均计算中的除数将在253处饱和,从而导致额外的数值误差。

示例:统计所有学生的平均总成绩
collect方式和mapToxx方式,源代码实现是不同的方式。建议使用mapToxx方式

@Test
public void testAveragingDouble() {
    Double averagingDouble = menu.stream().collect(Collectors.averagingDouble(Student::getTotalScore));
    Optional.ofNullable(averagingDouble).ifPresent(System.out::println); 
}
// 557.7142857142857

Double avg = list.stream().collect(Collectors.averagingDouble(Double::doubleValue));
Double avgs = list.stream().mapToDouble(Double::doubleValue).average().orElse(0D);
Double intAvg = integers.stream().mapToInt(Integer::intValue).average().orElse(0D);

2.collectingAndThen

调整收集器以执行额外的精加工转换

序号 修饰符和类型 方法和描述
4 static <T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher)
调整收集器以执行额外的精加工转换

collectingAndThen方法调整Collector收集器以执行其它的结束转换。例如,可以调整toList()收集器,以始终生成一个不可变的列表:

List<Student> studentList = menu.stream().collect(
    Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
System.out.println(studentList);

示例1:以指定字符串The Average totalScore is->输出所有学生的平均总成绩

@Test
public void testCollectingAndThen() {
    Optional.ofNullable(menu.stream().collect(
        Collectors.collectingAndThen(
            Collectors.averagingInt(Student::getTotalScore), a -> "The Average totalScore is->" + a)
    )).ifPresent(System.out::println);
}
// The Average totalScore is->557.7142857142857

示例2:将流中数据收集到treeSet中,然后将结果转换成ArrayList

List<Color> resList = list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(s -> s.getNum()))), ArrayList::new));

3.counting—统计数量

序号 修饰符和类型 方法和描述
5 static <T> Collector<T,?,Long> counting()
返回一个接受 T 类型元素的收集器,该元素计算输入元素的数量。

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

@Test
public void testCounting() {
    Optional.of(menu.stream().collect(Collectors.counting())).ifPresent(System.out::println);
}
// 7

long count = ls.stream().count();
System.out.println(count);

4.groupingBy—分组

注意4.4,用tomap分组

序号 修饰符和类型 方法和描述
6 static <T,K> Collector<T,?,Map<K,List<T>>> groupingBy(Function<? super T,? extends K> classifier)
返回一个对类型 T 的输入元素执行“分组依据”操作的收集器,根据分类函数对元素进行分组,并在 Map 中返回结果.
7 static <T,K,A,D> Collector<T,?,Map<K,D>> groupingBy(Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)
返回一个收集器,对类型 T 的输入元素执行级联“分组依据”操作,根据分类函数对元素进行分组,然后使用指定的下游收集器对与给定键关联的值执行归约操作
8 static <T,K,D,A,M extends Map<K,D>>Collector<T,?,M> groupingBy(Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream)
返回一个收集器,对类型 T 的输入元素执行级联“分组依据”操作,根据分类函数对元素进行分组,然后使用指定的下游收集器对与给定键关联的值执行归约操作

groupingBy(Function)
groupingBy(Function, Collector)
groupingBy(Function, Supplier, Collector)
这三个方法关系是依次递进的,都是返回一个Map>,
一个参数的,决定了map的key的取值,value用的就是流中的每个对象;
两个参数的key value都按照自己的方式取值;
三个参数的可以改变生成的map类型,改成treeMap类型等;;

4.1groupingBy(Function)

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

分类函数将元素映射到某些键类型K。收集器生成一个Map<K, List<T>>,其键是将分类函数应用于输入元素得到的值,其对应值为List,其中包含映射到分类函数下关联键的输入元素。
无法保证返回的Map或List对象的类型,可变性,可序列化或线程安全性。

注意: 返回的Collector收集器不是并发的。对于并行流管道,combiner函数通过将键从一个映射合并到另一个映射来操作,这可能是一个昂贵的操作。如果不需要保留元素出现在生成的Map收集器中的顺序,则使用groupingByConcurrent(Function)可以提供更好的并行性能。

示例:统计各个年级的学生信息

@Test
public void testGroupingByFunction() {
    Map<Student.GradeType, List<Student>> collect = menu.stream()
        .collect(Collectors.groupingBy(Student::getGradeType)); /*  !!! key取值为student的gradeType属性的值 !!! value 为流中每个student元素*/

    Optional.ofNullable(collect).ifPresent(System.out::println);
}
// {TWO=[Student{name='李四', totalScore=531, local=true, gradeType=TWO}], THREE=[Student{name='刘一', totalScore=721, local=true, gradeType=THREE}, Student{name='陈二', totalScore=637, local=true, gradeType=THREE}, Student{name='张三', totalScore=666, local=true, gradeType=THREE}, Student{name='王五', totalScore=483, local=false, gradeType=THREE}, Student{name='赵六', totalScore=367, local=true, gradeType=THREE}], ONE=[Student{name='孙七', totalScore=499, local=false, gradeType=ONE}]}

4.2groupingBy(Function, Collector)

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

分类函数将元素映射到某些键类型K。下游收集器对T类型的元素进行操作,并生成D类型的结果。产生收集器生成Map
返回的Map的类型,可变性,可序列化或线程安全性无法保证。

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

示例:统计各个年级的学生人数

@Test
public void testGroupingByFunctionAndCollector() {
    Optional.of(menu.stream()
        .collect(Collectors.groupingBy(Student::getGradeType, Collectors.counting()))) // key=student的gradeType属性,value=统计数量
        .ifPresent(System.out::println);
}
// {THREE=5, ONE=1, TWO=1}

4.3groupingBy(Function, Supplier, Collector)

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

分类函数将元素映射到某些键类型K。下游收集器对T类型的元素进行操作,并生成D类型的结果。产生收集器生成Map

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

示例:统计各个年级的平均成绩,并有序输出

@Test
public void testGroupingByFunctionAndSupplierAndCollector() {
    Map<Student.GradeType, Double> map = menu.stream()
        .collect(Collectors.groupingBy(
            Student::getGradeType, // key = student的gradeType属性值
            TreeMap::new,  //返回的容器时treeMap类型
            Collectors.averagingInt(Student::getTotalScore))); // value=分组后的平均值

    Optional.of(map.getClass()).ifPresent(System.out::println);
    Optional.of(map).ifPresent(System.out::println);
}
// class java.util.TreeMap
// {ONE=499.0, TWO=531.0, THREE=574.8}

注意这里的Supplier使用的是TreeMap,TreeMap是有序的。

[4.4] toMap 分组 []

collectors.toMap(参数1,参数2,参数三) 这个是将一个结合转换成map,map的key就是用参数1的funcation函数生成的;map的value就是用参数2的function函数生成的,参数三的作用就是如果key冲突的话,用参数三(BiFunction函数)将两个结果合并;(我们的示例是,如果冲突了就用后者代替前者;)

//接收一个 Function 参数
groupingBy(Function<? super T, ? extends K> classifier)

/**
 * Stream流数据--分组操作
 * 备注:切记Stream流只能被消费一次,流就失效了
 * 如下只是示例代码
 * @author liuzebiao
 * @Date 2020-1-10 13:37
 */
public class CollectDataToArray{

    public static void main(String[] args) {
        Stream<Student> studentStream = Stream.of(
                new Student("赵丽颖", 52, 56),
                new Student("杨颖", 56, 88),
                new Student("迪丽热巴", 56, 99),
                new Student("柳岩", 52, 53)
        );

        //1.按照具体年龄分组
        Map<Integer, List<Student>> map = studentStream.collect(Collectors.groupingBy((s -> s.getAge())));
        map.forEach((key,value)->{
            System.out.println(key + "---->"+value);
        });

        //2.按照分数>=60 分为"及格"一组  <60 分为"不及格"一组
        Map<String, List<Student>> map = studentStream.collect(Collectors.groupingBy(s -> {
            if (s.getScore() >= 60) {
                return "及格";
            } else {
                return "不及格";
            }
        }));
        map.forEach((key,value)->{
            System.out.println(key + "---->"+value);
        });


        //collectors.toMap(参数1,参数2,参数三)  这个是将一个结合转换成map,map的key就是用参数1的funcation函数生成的;map的value就是用参数2的function函数生成的,参数三的作用就是如果key冲突的话,用参数三(BiFunction函数)将两个结果合并;(我们的示例是,如果冲突了就用后者代替前者;)
        Map<Long, SetsOrgLevelSimpleInfoDto> dtoMap = orgs.stream().collect(Collectors.toMap(SetsOrgLevelSimpleInfoDto::getBranchId, o -> o, (o1, o2) -> o2));

    }
}

52---->[Student{name='赵丽颖', age=52, score=56}, Student{name='柳岩', age=52, score=53}]
56---->[Student{name='杨颖', age=56, score=88}, Student{name='迪丽热巴', age=56, score=99}]
-----------------------------------------------------------------------------------------------
不及格---->[Student{name='赵丽颖', age=52, score=56}, Student{name='柳岩', age=52, score=53}]
及格---->[Student{name='杨颖', age=56, score=88}, Student{name='迪丽热巴', age=56, score=99}]

4.5 多级分组

//接收两个参数: 1.Function 参数  2.Collector多级分组
groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream) 


    /**
 * Stream流数据--多级分组操作
 * 备注:切记Stream流只能被消费一次,流就失效了
 * 如下只是示例代码
 */
public class CollectDataToArray{

    public static void main(String[] args) {
        Stream<Student> studentStream = Stream.of(
                new Student("赵丽颖", 52, 95),
                new Student("杨颖", 56, 88),
                new Student("迪丽热巴", 56, 55),
                new Student("柳岩", 52, 33)
        );

        //多级分组
        //1.先根据年龄分组,然后再根据成绩分组
        //分析:第一个Collectors.groupingBy() 使用的是(年龄+成绩)两个维度分组,所以使用两个参数 groupingBy()方法
        //    第二个Collectors.groupingBy() 就是用成绩分组,使用一个参数 groupingBy() 方法
        Map<Integer, Map<Integer, Map<String, List<Student>>>> map = studentStream.collect(Collectors.groupingBy(str -> str.getAge(), Collectors.groupingBy(str -> str.getScore(), Collectors.groupingBy((student) -> {
            if (student.getScore() >= 60) {
                return "及格";
            } else {
                return "不及格";
            }
        }))));

        map.forEach((key,value)->{
            System.out.println("年龄:" + key);
            value.forEach((k2,v2)->{
                System.out.println("\t" + v2);
            });
        });
    }
}

年龄:52
    {不及格=[Student{name='柳岩', age=52, score=33}]}
    {及格=[Student{name='赵丽颖', age=52, score=95}]}
年龄:56
    {不及格=[Student{name='迪丽热巴', age=56, score=55}]}
    {及格=[Student{name='杨颖', age=56, score=88}]}

5.groupingByConcurrent

序号 修饰符和类型 方法和描述
9 static <T,K> Collector<T,?,ConcurrentMap<K,List<T>>> groupingByConcurrent(Function<? super T,? extends K> classifier)
返回对类型 T 的输入元素执行“分组依据”操作的并发收集器,根据分类函数对元素进行分组
10 static <T,K,A,D> Collector<T,?,ConcurrentMap<K,D>> groupingByConcurrent(Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)
返回一个并发收集器,对类型 T 的输入元素实现级联“分组依据”操作,根据分类函数对元素进行分组,然后使用指定的下游收集器对与给定键关联的值执行归约操作。
11 static <T,K,A,D,M extends ConcurrentMap<K,D>>Collector<T,?,M> groupingByConcurrent(Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream)
返回一个并发收集器,对类型 T 的输入元素实现级联“分组依据”操作,根据分类函数对元素进行分组,然后使用指定的下游收集器对与给定键关联的值执行归约操作

5.1groupingByConcurrent(Function)

groupingByConcurrent(Function)方法返回一个并发Collector收集器对T类型的输入元素执行”group by”操作,根据分类函数对元素进行分组

这是一个Collector.Characteristics#CONCURRENT并发和Collector.Characteristics#UNORDERED无序收集器。
分类函数将元素映射到某些键类型K。收集器生成一个ConcurrentMap,其键是将分类函数应用于输入元素得到的值,其对应值为List,其中包含映射到分类函数下关联键的输入元素。
无法保证返回的Map或List对象的类型,可变性或可序列化,或者返回的List对象的线程安全性。

5.2groupingByConcurrent(Function, Collector)

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

这是一个Collector.Characteristics#CONCURRENT并发和Collector.Characteristics#UNORDERED无序收集器。

分类函数将元素映射到某些键类型K。下游收集器对T类型的元素进行操作,并生成D类型的结果。产生收集器生成Map

5.3groupingByConcurrent(Function, Supplier, Collector)

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

这是一个Collector.Characteristics#CONCURRENT并发和Collector.Characteristics#UNORDERED无序收集器。

分类函数将元素映射到某些键类型K。下游收集器对T类型的元素进行操作,并生成D类型的结果。产生收集器生成Map

6.joining—拼接

//无参数—等价于 joining(“”);—空连接符
joining()
//一个参数————-指定连接符
joining(CharSequence delimiter)
//三个参数(前缀+后缀)———-指定连接符+前缀+后缀
joining(CharSequence delimiter, CharSequence prefix,CharSequence suffix)

6.1joining()

joining()方法返回一个Collector收集器,它按遇见顺序将输入元素连接成String。
示例:将所有学生的姓名连接成字符串

@Test
public void testJoining() {
    Optional.of(menu.stream().map(Student::getName).collect(Collectors.joining()))
        .ifPresent(System.out::println);
}
// 刘一陈二张三李四王五赵六孙七

6.2joining(delimiter)

joining(delimiter)方法返回一个Collector收集器,它以遇见顺序连接由指定分隔符分隔的输入元素。
示例:将所有学生的姓名以”,”分隔连接成字符串

@Test
public void testJoiningWithDelimiter() {
    Optional.of(menu.stream().map(Student::getName).collect(Collectors.joining(",")))
        .ifPresent(System.out::println);
}
// 刘一,陈二,张三,李四,王五,赵六,孙七

6.3joining(delimiter, prefix, suffix)

joining(delimiter, prefix, suffix)方法返回一个Collector收集器,它以遇见顺序将由指定分隔符分隔的输入元素与指定的前缀和后缀连接起来。
示例:将所有学生的姓名以”,”分隔,以Names[为前缀,]为后缀连接成字符串

@Test
public void testJoiningWithDelimiterAndPrefixAndSuffix() {
    Optional.of(menu.stream().map(Student::getName).collect(Collectors.joining(",", "Names[", "]")))
        .ifPresent(System.out::println);
}
// Names[刘一,陈二,张三,李四,王五,赵六,孙七]

7.mapping—映射

即:映射:先对集合中的元素进行映射,然后再对映射的结果使用Collectors操作

序号 修饰符和类型 方法和描述
15 static <T,U,A,R> Collector<T,?,R> mapping(Function<? super T,? extends U> mapper, Collector<? super U,A,R> downstream)
通过在累积之前对每个输入元素应用映射函数,将接受类型 U 的元素的收集器调整为接受类型为 T 的元素

mapping方法通过在累积之前将映射函数应用于每个输入元素,将Collector收集器接受U类型的元素调整为一个接受T类型的元素。
示例:将所有学生的姓名以”,”分隔连接成字符串

@Test
public void testMapping() {
    Optional.of(menu.stream().collect(Collectors.mapping(Student::getName, Collectors.joining(",", "Names[", "]")))) //先对流中每个元素执行funaction映射,然后对结果再执行collector操作(这里的操作时拼接,也可以干别的)
        .ifPresent(System.out::println);
}
// Names[刘一,陈二,张三,李四,王五,赵六,孙七]

@Test //这里是先转成map然后再拼接的方式;
public void testJoiningWithDelimiterAndPrefixAndSuffix() {
    Optional.of(menu.stream().map(Student::getName).collect(Collectors.joining(",", "Names[", "]")))
        .ifPresent(System.out::println);
}
// Names[刘一,陈二,张三,李四,王五,赵六,孙七]

两个方式效果是一样的;

8.maxBy—最大值

maxBy方法返回一个Collector收集器,它根据给定的Comparator比较器生成最大元素,描述为Optional<T>
示例:列出所有学生中成绩最高的学生信息

@Test
public void testMaxBy() {
    menu.stream().collect(Collectors.maxBy(Comparator.comparingInt(Student::getTotalScore)))
        .ifPresent(System.out::println);
}
// Student{name='刘一', totalScore=721, local=true, gradeType=THREE}


//获取最大值(Stream流 max()方法亦可)
//max()方法实现
//Optional<Student> max = studentStream.max((s1, s2) -> s1.getScore() - s2.getScore());

//(聚合)实现
Optional<Student> max = studentStream.collect(Collectors.maxBy((s1, s2) -> s1.getScore() - s2.getScore()));
System.out.println("最大值:"+max.get());

9.minBy—最小值

minBy方法返回一个Collector收集器,它根据给定的Comparator比较器生成最小元素,描述为Optional<T>
示例:列出所有学生中成绩最低的学生信息

@Test
public void testMinBy() {
    menu.stream().collect(Collectors.minBy(Comparator.comparingInt(Student::getTotalScore)))
        .ifPresent(System.out::println);
}
// Student{name='赵六', totalScore=367, local=true, gradeType=THREE}


//获取最小值(Stream流 min()方法亦可)
//min()方法实现
//Optional<Student> min = studentStream.max((s1, s2) -> s2.getScore() - s1.getScore());


//(聚合)实现
Optional<Student> min = studentStream.collect(Collectors.minBy((s1, s2) -> s1.getScore() - s2.getScore()));
System.out.println("最小值:"+min.get());

10.partitioningBy — 分区

0 分区分组区别

我们在前面学习了 Stream流中数据的分组操作,我们可以根据属性完成对数据的分组。接下来我们介绍分区操作,我们通过使用 Collectors.partitioningBy() ,根据返回值是否为 true,把集合分为两个列表,一个 true 列表,一个 false 列表分组和分区的区别就在:分组可以有多个组。分区只会有两个区( true 和 false)

序号 修饰符和类型 方法和描述
18 static <T> Collector<T,?,Map<Boolean,List<T>>> partitioningBy(Predicate<? super T> predicate)
返回一个收集器,它根据 Predicate 对输入元素进行分区,并将它们组织成 Map>
19 static <T,D,A> Collector<T,?,Map<Boolean,D>> partitioningBy(Predicate<? super T> predicate, Collector<? super T,A,D> downstream)
返回一个 Collector,它根据 Predicate 对输入元素进行分区,根据另一个 Collector 减少每个分区中的值,并将它们组织成一个 Map ,其值是下游减少的结果。

一个参数是一级分区
两个参数是多几分区

10.1partitioningBy(Predicate)()

partitioningBy(Predicate)方法返回一个Collector收集器,它根据Predicate对输入元素进行分区,并将它们组织成Map<Boolean, List<T>>

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

示例:列出所有学生中本地和非本地学生信息

List<Student> menu = Arrays.asList(
    new Student("刘一", 721, true, Student.GradeType.THREE),
    new Student("陈二", 637, true, Student.GradeType.THREE),
    new Student("张三", 666, true, Student.GradeType.THREE),
    new Student("李四", 531, true, Student.GradeType.TWO),
    new Student("王五", 483, false, Student.GradeType.THREE),
    new Student("赵六", 367, true, Student.GradeType.THREE),
    new Student("孙七", 499, false, Student.GradeType.ONE));
@Test
public void testPartitioningByWithPredicate() {
    Map<Boolean, List<Student>> collect = menu.stream()
        .collect(Collectors.partitioningBy(Student::isLocal));
    Optional.of(collect).ifPresent(System.out::println);
}
 {false=[Student{name='王五', totalScore=483, local=false, gradeType=THREE}, 
         Student{name='孙七', totalScore=499, local=false, gradeType=ONE}], 
  true=[Student{name='刘一', totalScore=721, local=true, gradeType=THREE}, 
        Student{name='陈二', totalScore=637, local=true, gradeType=THREE}, 
        Student{name='张三', totalScore=666, local=true, gradeType=THREE}, 
        Student{name='李四', totalScore=531, local=true, gradeType=TWO}, 
        Student{name='赵六', totalScore=367, local=true, gradeType=THREE}]}

我们在前面学习了 Stream流中数据的分组操作,我们可以根据属性完成对数据的分组。接下来我们介绍分区操作,我们通过使用 Collectors.partitioningBy() ,根据返回值是否为 true,把集合分为两个列表,一个 true 列表,一个 false 列表
分组和分区的区别就在:分组可以有多个组。分区只会有两个区( true 和 false)

//1.一个参数
partitioningBy(Predicate<? super T> predicate)

//2.两个参数(多级分区)
partitioningBy(Predicate<? super T> predicate, Collector<? super T, A, D> downstream)


public class CollectDataToArray{

    public static void main(String[] args) {
        Stream<Student> studentStream = Stream.of(
                new Student("赵丽颖", 52, 95),
                new Student("杨颖", 56, 88),
                new Student("迪丽热巴", 56, 55),
                new Student("柳岩", 52, 33)
        );

        //分区操作
        Map<Boolean, List<Student>> partitionMap = studentStream.collect(Collectors.partitioningBy(s -> s.getScore() > 60));
        partitionMap.forEach((key,value)->{
            System.out.println(key + "---->" + value);
        });
    }
}


false---->[Student{name='迪丽热巴', age=56, score=55}, Student{name='柳岩', age=52, score=33}]
true---->[Student{name='赵丽颖', age=52, score=95}, Student{name='杨颖', age=56, score=88}]

10.2partitioningBy(Predicate, Collector)

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

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

示例:列出所有学生中本地和非本地学生的平均总成绩

@Test
public void testPartitioningByWithPredicateAndCollector() {
    Map<Boolean, Double> collect = menu.stream()
        .collect(Collectors.partitioningBy(
                    Student::isLocal, 
                    Collectors.averagingInt(Student::getTotalScore)));
    Optional.of(collect).ifPresent(System.out::println);
}
// {false=491.0, true=584.4}

11.reducing

序号 修饰符和类型 方法和描述
20 static <T> Collector<T,?,Optional<T>> reducing(BinaryOperator<T> op)
返回一个收集器,它在指定的 BinaryOperator 下执行其输入元素的减少
21 static <T> Collector<T,?,T> reducing(T identity, BinaryOperator<T> op)
返回一个收集器,它使用提供的标识在指定的 BinaryOperator 下执行其输入元素的减少
22 static <T,U> Collector<T,?,U> reducing(T identity, BinaryOperator<T> op)
返回一个收集器,它使用提供的标识在指定的 BinaryOperator 下执行其输入元素的减少

11.1reducing(BinaryOperator)

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

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

示例:列出所有学生中成绩最高的学生信息

@Test
public void testReducingBinaryOperator() {
    menu.stream().collect(Collectors
                          .reducing(BinaryOperator
                                    .maxBy(Comparator
                                           .comparingInt(Student::getTotalScore))))
        .ifPresent(System.out::println);
}
// Student{name='刘一', totalScore=721, local=true, gradeType=THREE}

可直接使用reduce:列出所有学生中成绩最高的学生信息

@Test
public void testReducingBinaryOperator() {
    menu.stream().reduce(BinaryOperator
                         .maxBy(Comparator
                                .comparingInt(Student::getTotalScore)))
        .ifPresent(System.out::println);
}
// Student{name='刘一', totalScore=721, local=true, gradeType=THREE}

11.2reducing(Object, BinaryOperator)

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

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

示例:统计所有学生的总成绩

@Test
public void testReducingBinaryOperatorAndIdentiy() {
    Integer result = menu.stream()
        .map(Student::getTotalScore)
        .collect(Collectors.reducing(0, (d1, d2) -> d1 + d2));
    System.out.println(result);
}
// 3904

可直接使用reduce

@Test
public void testReducingBinaryOperatorAndIdentiy() {
    Integer result = menu.stream()
        .map(Student::getTotalScore).reduce(0, (d1, d2) -> d1 + d2);
    System.out.println(result);
}

11.3reducing(Object, Function, BinaryOperator)

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

reducing()相关收集器在groupingBy或partitioningBy下游的多级缩减中使用时非常有用。要对流执行简单缩减,请使用Stream#map(Function)和Stream#reduce(Object, BinaryOperator)。

示例:统计所有学生的总成绩

@Test
public void testReducingBinaryOperatorAndIdentiyAndFunction() {
    Integer result = menu.stream()
        .collect(Collectors.reducing(0, Student::getTotalScore, (d1, d2) -> d1 + d2));
    System.out.println(result);
}
// 3904

可直接使用reduce

@Test
public void testReducingBinaryOperatorAndIdentiyAndFunction() {
    Integer result = menu.stream()
        .map(Student::getTotalScore)
        .reduce(0, (d1, d2) -> d1 + d2);
    System.out.println(result);
}

12.summarizingDouble

summarizingDouble方法返回一个Collector收集器,它将double生成映射函数应用于每个输入元素,并返回结果值的摘要统计信息。

示例:统计所有学生的摘要信息(总人数,总成绩,最高成绩,最低成绩和平均成绩)

List<Student> menu = Arrays.asList(
    new Student("刘一", 721, true, Student.GradeType.THREE),
    new Student("陈二", 637, true, Student.GradeType.THREE),
    new Student("张三", 666, true, Student.GradeType.THREE),
    new Student("李四", 531, true, Student.GradeType.TWO),
    new Student("王五", 483, false, Student.GradeType.THREE),
    new Student("赵六", 367, true, Student.GradeType.THREE),
    new Student("孙七", 499, false, Student.GradeType.ONE));

@Test
public void testSummarizingInt() {
    DoubleSummaryStatistics result = menu.stream()
            .collect(Collectors.summarizingDouble(Student::getTotalScore));
    Optional.of(result).ifPresent(System.out::println);
}
// DoubleSummaryStatistics{count=7, sum=3904.000000, min=367.000000, average=557.714286, max=721.000000}
// IntSummaryStatistics{count=7, sum=3904, min=367, average=557.714286, max=721}
// LongSummaryStatistics{count=7, sum=3904, min=367, average=557.714286, max=721}

13.summingDouble

返回一个Collector收集器,它生成应用于输入元素的double值函数的总和。如果没有元素,则结果为0。

返回的总和可能会因记录值的顺序而变化,这是由于除了不同大小的值之外,还存在累积舍入误差。通过增加绝对量排序的值(即总量,样本越大,结果越准确)往往会产生更准确的结果。如果任何记录的值是NaN或者总和在任何点NaN,那么总和将是NaN。

示例:统计所有学生的总成绩

@Test
public void testSummingDouble() {
    Optional.of(menu.stream().collect(Collectors.summingDouble(Student::getTotalScore)))
        .ifPresent(System.out::println);
}
// 3904.0
// 3904
// 3904

14.toCollection

返回一个Collector收集器,它按遇见顺序将输入元素累积到一个新的Collection收集器中。Collection收集器由提供的工厂创建。

示例:统计总分大于600的所有学生的信息放入LinkedList中

@Test
public void testToCollection() {
    Optional.of(menu.stream().filter(d -> d.getTotalScore() > 600)
                .collect(Collectors.toCollection(LinkedList::new)))
        .ifPresent(v -> {
              System.out.println(v.getClass());
            System.out.println(v);
         });
}
// class java.util.LinkedList
// [Student{name='刘一', totalScore=721, local=true, gradeType=THREE}, Student{name='陈二', totalScore=637, local=true, gradeType=THREE}, Student{name='张三', totalScore=666, local=true, gradeType=THREE}]

15.toConcurrentMap

15.1toConcurrentMap(Function, Function)

返回一个并发的Collector收集器,它将元素累积到ConcurrentMap中,其键和值是将提供的映射函数应用于输入元素的结果。

如果映射的键包含重复项(根据Object#equals(Object)),则在执行收集操作时会抛出IllegalStateException。如果映射的键可能有重复,请使用toConcurrentMap(Function, Function, BinaryOperator)。

注意: 键或值作为输入元素是常见的。在这种情况下,实用方法java.util.function.Function#identity()可能会有所帮助。

这是一个Collector.Characteristics#CONCURRENT并发和Collector.Characteristics#UNORDERED无序收集器。

示例:以学生姓名为键总分为值统计信息

@Test
public void testToConcurrentMap() {
    Optional.of(menu.stream()
                .collect(Collectors.toConcurrentMap(Student::getName, Student::getTotalScore)))
        .ifPresent(v -> {
            System.out.println(v);
            System.out.println(v.getClass());
        });
}
// {李四=531, 孙七=499, 刘一=721, 张三=666, 陈二=637, 王五=483, 赵六=367}
// class java.util.concurrent.ConcurrentHashMap

15.2toConcurrentMap(Function, Function, BinaryOperator)

返回一个并发的Collector收集器,它将元素累积到ConcurrentMap中,其键和值是将提供的映射函数应用于输入元素的结果。

如果映射的键包含重复项(根据Object#equals(Object)),则将值映射函数应用于每个相等的元素,并使用提供的合并函数合并结果。

注意: 有多种方法可以处理映射到同一个键的多个元素之间的冲突。toConcurrentMap的其它形式只是使用无条件抛出的合并函数,但你可以轻松编写更灵活的合并策略。例如,如果你有一个Person流,并且你希望生成一个“电话簿”映射名称到地址,但可能有两个人具有相同的名称,你可以按照以下方式进行优雅的处理这些冲突,并生成一个Map将名称映射到连接的地址列表中:

Map phoneBook
people.stream().collect(toConcurrentMap(Person::getName,
Person::getAddress,
(s, a) -> s + “, “ + a));

这是一个Collector.Characteristics#CONCURRENT并发和Collector.Characteristics#UNORDERED无序收集器。

示例:以年级为键学生人数为值统计信息

/**
 * GradeType:TotalNum
 */
@Test
public void testToConcurrentMapWithBinaryOperator() {
    Optional.of(menu.stream()
                .collect(Collectors.toConcurrentMap(Student::getGradeType, v -> 1L, (a, b) -> a + b)))
        .ifPresent(v -> {
            System.out.println(v);
            System.out.println(v.getClass());
        });
}
// {ONE=1, THREE=5, TWO=1}
// class java.util.concurrent.ConcurrentHashMap

15.3toConcurrentMap(Function, Function, BinaryOperator, Supplier)

返回一个并发的Collector收集器,它将元素累积到ConcurrentMap中,其键和值是将提供的映射函数应用于输入元素的结果。

如果映射的键包含重复项(根据Object#equals(Object)),则将值映射函数应用于每个相等的元素,并使用提供的合并函数合并结果。ConcurrentMap由提供的供应商函数创建。

这是一个Collector.Characteristics#CONCURRENT并发和Collector.Characteristics#UNORDERED无序收集器。

示例:以年级为键学生人数为值将统计的信息放入ConcurrentSkipListMap中

/**
 * GradeType:TotalNum
 */
@Test
public void testToConcurrentMapWithBinaryOperatorAndSupplier() {
    Optional.of(menu.stream()
            .collect(Collectors.toConcurrentMap(
                    Student::getGradeType, 
                    v -> 1L, 
                    (a, b) -> a + b, 
                    ConcurrentSkipListMap::new)))
            .ifPresent(v -> {
                System.out.println(v);
                System.out.println(v.getClass());
            });
}
// {ONE=1, TWO=1, THREE=5}
// class java.util.concurrent.ConcurrentSkipListMap

16.toList

返回一个Collector收集器,它将输入元素累积到一个新的List中。返回的List的类型,可变性,可序列化或线程安全性无法保证;如果需要更多地控制返回的List,请使用toCollection(Supplier)。

示例:查出本地学生的信息并放入ArrayList中

@Test
public void testToList() {
    Optional.of(menu.stream().filter(Student::isLocal).collect(Collectors.toList()))
        .ifPresent(r -> {
            System.out.println(r.getClass());
            System.out.println(r);
        });
}
// class java.util.ArrayList
// [Student{name='刘一', totalScore=721, local=true, gradeType=THREE}, Student{name='陈二', totalScore=637, local=true, gradeType=THREE}, Student{name='张三', totalScore=666, local=true, gradeType=THREE}, Student{name='李四', totalScore=531, local=true, gradeType=TWO}, Student{name='赵六', totalScore=367, local=true, gradeType=THREE}]

17.toMap

17.1toMap(Function, Function)

返回一个Collector收集器,它将元素累积到Map中,其键和值是将提供的映射函数应用于输入元素的结果。

如果映射的键包含重复项(根据Object#equals(Object)),则在执行收集操作时会抛出IllegalStateException。如果映射的键可能有重复,请使用toMap(Function, Function, BinaryOperator)。

注意: 键或值作为输入元素是常见的。在这种情况下,实用方法java.util.function.Function#identity()可能会有所帮助。

返回的Collector收集器不是并发的。对于并行流管道,combiner函数通过将键从一个映射合并到另一个映射来操作,这可能是一个昂贵的操作。

如果不需要将结果以遇见的顺序插入Map,则使用toConcurrentMap(Function, Function)可以提供更好的并行性能。

17.2toMap(Function, Function, BinaryOperator)

返回一个并发的Collector收集器,它将元素累积到Map中,其键和值是将提供的映射函数应用于输入元素的结果。

如果映射的键包含重复项(根据Object#equals(Object)),则将值映射函数应用于每个相等的元素,并使用提供的合并函数合并结果。

注意: 有多种方法可以处理映射到同一个键的多个元素之间的冲突。toMap的其它形式只是使用无条件抛出的合并函数,但你可以轻松编写更灵活的合并策略。例如,如果你有一个Person流,并且你希望生成一个“电话簿”映射名称到地址,但可能有两个人具有相同的名称,你可以按照以下方式进行优雅的处理这些冲突,并生成一个Map将名称映射到连接的地址列表中:

Map<String, String> phoneBook
 people.stream().collect(toConcurrentMap(Person::getName,
                                         Person::getAddress,
                                         (s, a) -> s + ", " + a));

返回的Collector收集器不是并发的。对于并行流管道,combiner函数通过将键从一个映射合并到另一个映射来操作,这可能是一个昂贵的操作。

如果不需要将结果以遇见的顺序插入Map,则使用toConcurrentMap(Function, Function, BinaryOperator)可以提供更好的并行性能。

17.3toMap(Function, Function, BinaryOperator, Supplier)

返回一个并发的Collector收集器,它将元素累积到Map中,其键和值是将提供的映射函数应用于输入元素的结果。

如果映射的键包含重复项(根据Object#equals(Object)),则将值映射函数应用于每个相等的元素,并使用提供的合并函数合并结果。Map由提供的供应商函数创建。

注意: 返回的Collector收集器不是并发的。对于并行流管道,combiner函数通过将键从一个映射合并到另一个映射来操作,这可能是一个昂贵的操作。

如果不需要将结果以遇见的顺序插入Map,则使用toConcurrentMap(Function, Function, BinaryOperator, Supplier)可以提供更好的并行性能。

18.toSet

返回一个Collector收集器,它将输入元素累积到一个新的Set中。返回的Set的类型,可变性,可序列化或线程安全性无法保证;如果需要更多地控制返回的Set,请使用toCollection(Supplier)。

这是一个Collector.Characteristics#UNORDERED无序收集器。

示例:查出本地学生的信息并放入HashSet中

@Test
public void testToSet() {
    Optional.of(menu.stream().filter(Student::isLocal).collect(Collectors.toSet()))
        .ifPresent(r -> {
            System.out.println(r.getClass());
            System.out.println(r);
        });
}
// class java.util.HashSet
// [Student{name='张三', totalScore=666, local=true, gradeType=THREE}, Student{name='陈二', totalScore=637, local=true, gradeType=THREE}, Student{name='刘一', totalScore=721, local=true, gradeType=THREE}, Student{name='李四', totalScore=531, local=true, gradeType=TWO}, Student{name='赵六', totalScore=367, local=true, gradeType=THREE}]