Java8 -- 05 -- Collectors类常用方法解析

14 篇文章 10 订阅

原文链接:Java8 – 05 – Collectors类常用方法解析


相关文章:


在使用流之前,我们先来了解下 Collectors 类,因为在使用流的过程中,会经常用到该类的相关方法

Collectors 工具类提供了许多静态工具方法来创建收集器,比如将元素装进一个集合中、将元素分组、根据不同标准对元素进行汇总等,现在我们就来看看它具体使用


  • Student.java

    public class Student {
    
        private String name;
        private int age;
        private double score;
    
        public Student(String name, int age, int score){
            this.name = name;
            this.age = age;
            this.score = score;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public double getScore() {
            return score;
        }
    
        public void setScore(double score) {
            this.score = score;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", score=" + score +
                    '}';
        }
    }
    

一、toList()

  • 将流中所有元素收集到一个 List 中

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
            
    List<String> studentNameList = studentList.stream().map(Student::getName).collect(toList());
    // [小白, 小黑, 小红, 小明]
    System.out.println(studentNameList);
    

二、toMap()

  • 将流中所有元素收集到一个 Map 中

  • 该方法有四个参数

    • Function<? super T, ? extends K> keyMapper

      • 映射函数,用于生成 key
    • Function<? super T, ? extends U> valueMapper

      • 映射函数,用于生成 value
    • BinaryOperator<U> mergeFunction

      • 合并函数,用于解决 key 重复的情况
    • Supplier<M> mapSupplier

      • 供给函数,返回一个空的 Map,用于存放 key、value,默认实现为 HashMap::new
  • 该方法有三个重载方法,为了方便展示我们将参数类型省去

    • toMap(keyMapper, valueMapper)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      Map<String, Integer> studentMap = studentList.stream()
          .collect(toMap(Student::getName, Student::getAge));
      // {小明=22, 小白=20, 小红=22, 小黑=21}
      System.out.println(studentMap);
      
      • 此处以学生姓名作为 key,学生年龄作为 value,存放到 Map 中
    • toMap(keyMapper, valueMapper, mergeFunction)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      Map<Integer, String> studentMap = studentList.stream()
          .collect(toMap(Student::getAge, Student::getName, (oldValue, newValue) -> {
              // 【小红】被替换成了【小明】
              System.out.println("【" + oldValue + "】被替换成了【" + newValue + "】");
              return newValue;
          }));
      // {20=小白, 21=小黑, 22=小明}
      System.out.println(studentMap);
      
      • 此处以学生年龄作为 key,会存在 key 重复的情况,如果不指定合并函数的话,程序会报异常

      • 此处当 key 重复时,取新 value 覆盖旧 value,从输出中我们可以很清晰地看到 value 的替换过程

    • toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      Map<Integer, String> studentMap = studentList.stream()
          .collect(toMap(Student::getAge, Student::getName,
          (oldValue, newValue) -> newValue, LinkedHashMap::new));
      // {20=小白, 21=小黑, 22=小明}
      System.out.println(studentMap);
      
      • 此处我们将默认的 HashMap::new 替换成 LinkedHashMap::new,使用 LinkedHashMap 来存储数据

三、toConcurrentMap()

  • toConcurrentMap() 用法与 toMap() 用法相同

  • 不同之处在于 toConcurrentMap() 返回的是 ConcurrentMap,toMap() 返回的是 Map


四、groupingBy()

  • 根据流中元素的某个属性值对流中元素进行分组,并将该属性值作为结果 Map 的 key

  • 该方法有三个参数

    • Function<? super T, ? extends K> classifier

      • 分类函数,用于将输入元素映射到 key
    • Supplier<M> mapFactory

      • 供给函数,返回一个空的 Map,用于存放 key、value,默认实现为 HashMap::new
    • Collector<? super T, A, D> downstream

      • 归约收集器
  • 该方法有三个重载方法,为了方便展示我们将参数类型省去

    • groupingBy(classifier)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      Map<Integer, List<Student>> studentMap = studentList
          .stream().collect(groupingBy(Student::getAge));
      // {20=[Student(name=小白, age=20, score=90.0)], 
      // 21=[Student(name=小黑, age=21, score=95.0)],
      // 22=[Student(name=小红, age=22, score=80.0), Student(name=小明, age=22, score=82.0)]}
      System.out.println(studentMap);
      
      • 此处以学生年龄作为分组条件对集合进行分组
    • groupingBy(classifier, downstream)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      Map<Integer, Long> studentMap = studentList
          .stream().collect(groupingBy(Student::getAge, counting()));
      // {20=1, 21=1, 22=2}
      System.out.println(studentMap);
      
      • 此处以学生年龄作为分组条件对集合进行分组,然后再对分组结果进行归约统计
    • groupingBy(classifier, mapFactory, downstream)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      Map<Integer, Long> studentMap = studentList
          .stream().collect(groupingBy(Student::getAge, LinkedHashMap::new, counting()));
      // {20=1, 21=1, 22=2}
      System.out.println(studentMap);
      
      • 此处我们将默认的 HashMap::new 替换成 LinkedHashMap::new,使用 LinkedHashMap 来存储数据
  • 此外,我们可以将多个 groupingBy 嵌套使用,从而实现多级分组

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    Map<Integer, Map<Double, List<Student>>> studentMap = studentList
        .stream().collect(groupingBy(Student::getAge, groupingBy(Student::getScore)));
    // {20={90.0=[Student(name=小白, age=20, score=90.0)]}, 
    // 21={95.0=[Student(name=小黑, age=21, score=95.0)]}, 
    // 22={82.0=[Student(name=小明, age=22, score=82.0)], 80.0=[Student(name=小红, age=22, score=80.0)]}}
    System.out.println(studentMap);
    
    • 此处以学生年龄作为分组条件对集合进行分组,再以学生分数作为分组条件对集合进行二次分组

五、groupingByConcurrent()

  • groupingByConcurrent() 用法与 groupingBy() 用法相同

  • 不同之处在于 groupingByConcurrent() 返回的是 ConcurrentMap,groupingBy() 返回的是 Map


六、counting()

  • 计算流中元素的个数

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    Long studentCount = studentList.stream().collect(counting());
    System.out.println(studentCount); // 4
    

七、joining()

  • 连接对流中每个元素调用 toString() 方法后所生成的字符串

  • 该方法有三个参数

    • CharSequence delimiter

      • 每个元素之间的分隔符
    • CharSequence prefix

      • 连接结果的前缀
    • CharSequence suffix

      • 连接结果的后缀
  • 该方法有三个重载方法,为了方便展示我们将参数类型省去

    • joining()

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      String studentJoining = studentList.stream()
          .map(Student::getName).collect(joining());
      System.out.println(studentJoining); // 小白小黑小红小明
      
    • joining(delimiter)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      String studentJoining = studentList.stream()
          .map(Student::getName).collect(joining("@"));
      System.out.println(studentJoining); // 小白@小黑@小红@小明
      
    • joining(delimiter, prefix, suffix)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      String studentJoining = studentList.stream()
          .map(Student::getName).collect(joining("@", "111", "222"));
      System.out.println(studentJoining); // 111小白@小黑@小红@小明222
      

八、toSet()

  • 将流中所有元素收集到一个 Set 中,并去除重复项

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    Set<Integer> studentAgeSet = studentList.stream()
        .map(Student::getAge).collect(toSet());
    System.out.println(studentAgeSet); // [20, 21, 22]
    

九、summarizingDouble()

  • 收集流中元素 Double 属性字段的统计值,如:最大值、最小值、总和、平均值

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    DoubleSummaryStatistics doubleSummaryStatistics = studentList
        .stream().collect(summarizingDouble(Student::getScore));
    // DoubleSummaryStatistics{count=4, sum=347.000000, 
    // min=80.000000, average=86.750000, max=95.000000}
    System.out.println(doubleSummaryStatistics);
    

十、summarizingInt()

  • 收集流中元素 Integer 属性字段的统计值,如:最大值、最小值、总和、平均值

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    IntSummaryStatistics intSummaryStatistics = studentList
            .stream().collect(summarizingInt(Student::getAge));
    // IntSummaryStatistics{count=4, sum=85, 
    // min=20, average=21.250000, max=22}
    System.out.println(intSummaryStatistics);
    

十一、summarizingLong()

  • 收集流中元素 Long 属性字段的统计值,如:最大值、最小值、总和、平均值

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    LongSummaryStatistics longSummaryStatistics = studentList
            .stream().collect(summarizingLong(Student::getAge));
    // IntSummaryStatistics{count=4, sum=85,
    // min=20, average=21.250000, max=22}
    System.out.println(longSummaryStatistics);
    

十二、averagingDouble()

  • 计算流中元素 Double 属性字段的平均值

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    Double averageScore = studentList.stream().collect(averagingDouble(Student::getScore));
    System.out.println(averageScore); // 86.75
    

十三、averagingInt()

  • 计算流中元素 Integer 属性字段的平均值

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    Double averageAge = studentList.stream().collect(averagingInt(Student::getAge));
    System.out.println(averageAge); // 21.25
    

十四、averagingLong()

  • 计算流中元素 Long 属性字段的平均值

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    Double averageAge = studentList.stream().collect(averagingLong(Student::getAge));
    System.out.println(averageAge); // 21.25
    

十五、summingDouble()

  • 计算流中元素 Double 属性字段的总和

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    Double summingScore = studentList.stream().collect(summingDouble(Student::getScore));
    System.out.println(summingScore); // 347.0
    

十六、summingInt()

  • 计算流中元素 Integer 属性字段的总和

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    Integer summingAge = studentList.stream().collect(summingInt(Student::getAge));
    System.out.println(summingAge); // 85
    

十七、summingLong()

  • 计算流中元素 Long 属性字段的总和

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    Long summingAge = studentList.stream().collect(summingLong(Student::getAge));
    System.out.println(summingAge); // 85
    

十八、reducing()

  • 从一个作为累加器的初始值开始,将流归约为单个值

  • 该方法有三个参数

    • U identity

      • 初始值
    • Function<? super T, ? extends U> mapper

      • 映射函数,应用于每个输入值
    • BinaryOperator<U> op

      • 计算函数,接收两个参数,返回一个相同类型的值
  • 该方法有三个重载方法,为了方便展示我们将参数类型省去

    • reducing(op)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      Optional<Double> reducingScore = studentList.stream()
          .map(Student::getScore).collect(reducing((x, y) -> x + y));
      System.out.println(reducingScore.get()); // 347.0
      
      • 此处我们对学生分数进行归约累加
    • reducing(identity, op)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      Double reducingScore = studentList.stream().map(Student::getScore)
          .collect(reducing(3.0, (x, y) -> x + y));
      System.out.println(reducingScore); // 350.0
      
      • 此处我们以 3.0 作为初始值,然后再对学生分数进行规约累加
    • reducing(identity, mapper, op)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      Double reducingScore = studentList.stream().map(Student::getScore)
          .collect(reducing(3.0, x -> x * 2, (x, y) -> x + y));
      System.out.println(reducingScore); // 697.0
      
      • 此处我们以 3.0 作为初始值,然后再将学生分数都乘以 2,最后再对学生分数进行规约累加

十九、maxBy()

  • 将流按照给定的比较器筛选出最大元素,用 Optional 进行包裹
    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    Optional<Student> studentMaxOptional = studentList.stream()
        .collect(maxBy(comparingDouble(Student::getScore)));
    // Student(name=小黑, age=21, score=95.0)
    System.out.println(studentMaxOptional.get());
    

二十、minBy()

  • 将流按照给定的比较器筛选出最小元素,用 Optional 进行包裹

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    Optional<Student> studentMinOptional = studentList.stream()
        .collect(minBy(comparingDouble(Student::getScore)));
    // Student(name=小红, age=22, score=80.0)
    System.out.println(studentMinOptional.get());
    

二十一、partitioningBy()

  • 根据流中每个元素应用谓词的结果来对元素进行分组 (只会分为 true 和 false 两组)

  • 该方法有两个参数

    • Predicate<? super T> predicate

      • 断言函数,用于对输入元素进行分类
    • Collector<? super T, A, D> downstream

      • 归约收集器
  • 该方法有两个重载方法,为了方便展示我们将参数类型省去

    • partitioningBy(predicate)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      Map<Boolean, List<Student>> studentMap  = studentList.stream()
          .collect(partitioningBy(student -> student.getScore() >= 90));
      // {false=[Student(name=小红, age=22, score=80.0), Student(name=小明, age=22, score=82.0)],
      // true=[Student(name=小白, age=20, score=90.0), Student(name=小黑, age=21, score=95.0)]}
      System.out.println(studentMap);
      
      • 此处我们以学生分数 >=90 作为分区条件对集合进行分组
    • partitioningBy(predicate, downstream)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      Map<Boolean, Map<Integer, Long>> studentMap = studentList.stream()
          .collect(partitioningBy(student -> student.getScore() >= 90,
          groupingBy(Student::getAge, counting())));
      // {false={22=2}, true={20=1, 21=1}}
      System.out.println(studentMap);
      
      • 此处我们以学生分数 >=90 作为分区条件对集合进行分组,然后再对分组结果进行归约统计

二十二、collectingAndThen()

  • 包裹一个收集器,对其结果应用转换函数

  • 该方法有两个参数

    • Collector<T,A,R> downstream

      • 收集器
    • Function<R,RR> finisher

      • 转换函数
  • 该方法有一个重载方法,为了方便展示我们将参数类型省去

    • collectingAndThen(downstream, finisher)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      Integer listSize = studentList.stream()
          .collect(collectingAndThen(toList(), List::size));
      System.out.println(listSize); // 4
      
      • 此处我们将 toList() 方法得到的结果转换为了其长度

二十三、mapping()

  • 将流中的每个元素转换为指定类型的元素

  • 该方法有两个参数

    • Function<? super T, ? extends U> mapper

      • 映射函数,对流中元素进行转换
    • Collector<? super U, A, R> downstream

      • 归约收集器
  • 该方法有一个重载方法,为了方便展示我们将参数类型省去

    • mapping(mapper, downstream)

      List<Student> studentList = new ArrayList<>(Arrays.asList(
              new Student("小白", 20, 90),
              new Student("小黑", 21, 95),
              new Student("小红", 22, 80),
              new Student("小明", 22, 82)));
      
      Map<Integer, ArrayList<String>> mapping = studentList.stream()
          .collect(groupingBy(Student::getAge, mapping(student -> {
              double score = student.getScore();
              String name = student.getName();
              if (score >= 90) {
                  return name + ": 优秀";
              } else if (score >= 80 && score < 90) {
                  return name + ": 良好";
              } else if (score >= 60 && score < 80) {
                  return name + ": 及格";
              } else {
                  return name + ": 不及格";
              }
      }, toCollection(ArrayList::new))));
      // {20=[小白: 优秀], 21=[小黑: 优秀], 22=[小红: 良好, 小明: 良好]}
      System.out.println(mapping);
      
      • 此处我们先按照年龄对学生进行分组,然后再按分数高低分别映射为优秀、良好、及格和不及格,并使用 ArrayList 进行接收

二十四、toCollection()

  • 将流中所有元素收集到指定容器

    List<Student> studentList = new ArrayList<>(Arrays.asList(
            new Student("小白", 20, 90),
            new Student("小黑", 21, 95),
            new Student("小红", 22, 80),
            new Student("小明", 22, 82)));
    
    HashSet<String> studentNameSet = studentList.stream()
        .map(Student::getName).collect(toCollection(HashSet::new));
    // [小明, 小白, 小红, 小黑]
    System.out.println(studentNameSet);
    
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值