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