前言
在后面的文章中其实大家也曾经看到我应用过 collect(Collectors.toList()) 将数据最初汇总成一个 List 汇合。
但其实还能够转换成 Integer、Map、Set 汇合等。
一、查找流中的最大值和最小值
static List<Student> students = new ArrayList<>();
static {
students.add(new Student(“ 学生 A ”, “ 大学 A ”, 18, 98.0));
students.add(new Student(“ 学生 B ”, “ 大学 A ”, 18, 91.0));
students.add(new Student(“ 学生 C ”, “ 大学 A ”, 18, 90.0));
students.add(new Student(“ 学生 D ”, “ 大学 B ”, 18, 76.0));
students.add(new Student(“ 学生 E ”, “ 大学 B ”, 18, 91.0));
students.add(new Student(“ 学生 F ”, “ 大学 B ”, 19, 65.0));
students.add(new Student(“ 学生 G ”, “ 大学 C ”, 20, 80.0));
students.add(new Student(“ 学生 H ”, “ 大学 C ”, 21, 78.0));
students.add(new Student(“ 学生 I ”, “ 大学 C ”, 20, 67.0));
students.add(new Student(“ 学生 J ”, “ 大学 D ”, 22, 87.0));
}
public static void main(String[] args) {
Optional<Student> collect1 = students.stream().collect(Collectors.maxBy((s1, s2) -> s1.getAge() – s2.getAge()));
Optional<Student> collect2 = students.stream().collect(Collectors.minBy((s1, s2) -> s1.getAge() – s2.getAge()));
Student max = collect1.get();
Student min = collect2.get();
System.out.println(“max 年龄的学生 ==>” + max);
System.out.println(“min 年龄的学生 ==>” + min);
/**
* max 年龄的学生 ==>Student(name= 学生 J, school= 大学 D, age=22, score=87.0)
* min 年龄的学生 ==>Student(name= 学生 A, school= 大学 A, age=18, score=98.0)
*/
}
复制代码
Optional,它是一个容器,能够蕴含也能够不蕴含值。它是 java8 中人们常说的优雅的判空的操作。
另一个常见的返回单个值的归约操作是对流中对象的一个数值字段求和。或者你可能想要求 平均数。这种操作被称为汇总操作。让咱们来看看如何应用收集器来表白汇总操作。
二、汇总
Collectors 类专门为汇总提供了一些个工厂办法:
当然除此之外还有 求平均数 averagingDouble、求总数 counting 等等
咱们暂且就先以 summingDouble 和 summarizingDouble 来举例吧
案例数据依然是下面的那些 student 数据 …
求全副学生问题的总分,求全副学生的平均分。
1、首先应用 summingDouble 和 averagingDouble 来实现
Double summingScore = students.stream().collect(Collectors.summingDouble(Student::getScore));
Double averagingScore = students.stream().collect(Collectors.averagingDouble(Student::getScore));
System.out.println(“ 学生的总分 ==>” + summingScore);
System.out.println(“ 学生的平均分 ==>” + averagingScore);
/**
* 学生的总分 ==>823.0
* 学生的平均分 ==>82.3
*/
复制代码
2、应用 summarizingDouble 来实现
它更为综合,能够间接计算出相干的汇总信息
DoubleSummaryStatistics summarizingDouble = students.stream().collect(Collectors.summarizingDouble(Student::getScore));
double sum = summarizingDouble.getSum();
long count = summarizingDouble.getCount();
double average = summarizingDouble.getAverage();
double max = summarizingDouble.getMax();
double min = summarizingDouble.getMin();
System.out.println(“sum==>”+sum);
System.out.println(“count==>”+count);
System.out.println(“average==>”+average);
System.out.println(“max==>”+max);
System.out.println(“min==>”+min);
/**
* sum==>823.0
* count==>10
* average==>82.3
* max==>98.0
* min==>65.0
*/
复制代码
但其实大家也都发现了,应用一个接口可能实现,也能够拆开依据本人的所需,抉择适合的 API 来实现,具体的应用还是须要看应用场景。
三、连贯字符串
Joining,就是把流中每一个对象利用 toString 办法失去的所有字符串连接成一个字符串。
如果这么看,它其实没啥用,然而 Java 也留下了后招,它的伙伴(重载办法)提供了一个能够承受元素之间的宰割符的办法。
String studentsName = students.stream().map(student -> student.getName()).collect(Collectors.joining());
System.out.println(studentsName);
String studentsName2 = students.stream().map(student -> student.getName()).collect(Collectors.joining(“,”));
System.out.println(studentsName2);
/**
* 学生 A 学生 B 学生 C 学生 D 学生 E 学生 F 学生 G 学生 H 学生 I 学生 J
* 学生 A, 学生 B, 学生 C, 学生 D, 学生 E, 学生 F, 学生 G, 学生 H, 学生 I, 学生 J
*/
复制代码
对于对象的打印:
// 不过对于对象的打印 个人感觉还好 哈哈
String collect = students.stream().map(student -> student.toString()).collect(Collectors.joining(“,”));
System.out.println(collect);
System.out.println(students);
/**
* Student(name= 学生 A, school= 大学 A, age=18, score=98.0),Student(name= 学生 B, school= 大学 A, age=18, score=91.0),Student(name= 学生 C, school= 大学 A, age=18, score=90.0),Student(name= 学生 D, school= 大学 B, age=18, score=76.0),Student(name= 学生 E, school= 大学 B, age=18, score=91.0)….
* [Student(name= 学生 A, school= 大学 A, age=18, score=98.0), Student(name= 学生 B, school= 大学 A, age=18, score=91.0), Student(name= 学生 C, school= 大学 A, age=18, score=90.0), Student(name= 学生 D, school= 大学 B, age=18, score=76.0)..)]
*/
复制代码
但其实我还有一些没有讲到的 API 应用办法,大家也能够额定去尝试尝试,这其实远比你看这篇文章排汇的更快~
四、分组
就像数据库中的分组统计一样~
1、分组
举个例子,我想统计每个学校有哪些学生
我是不是得设计这样的一个数据结构 Map<String,List<Student>> 能力寄存勒,我在循环的时候,是不是每次都得判断一下学生所在的学校的名称,而后看是否要给它增加到这个 List 汇合中去,最初再 put 到 map 中去呢?
看着就特地繁琐,然而在 stream 中就变成了一行代码,其余的货色,都是 Java 外部给你优化了。
// 我想晓得每所学校中,学生的数量及相干信息,只有这一行代码即可
Map<String, List<Student>> collect = students.stream().collect(Collectors.groupingBy(Student::getSchool));
System.out.println(collect);
/**
* {大学 B =[Student(name= 学生 D, school= 大学 B, age=18, score=76.0), Student(name= 学生 E, school= 大学 B, age=18, score=91.0), Student(name= 学生 F, school= 大学 B, age=19, score=65.0)],
* 大学 A =[Student(name= 学生 A, school= 大学 A, age=18, score=98.0), Student(name= 学生 B, school= 大学 A, age=18, score=91.0), Student(name= 学生 C, school= 大学 A, age=18, score=90.0)],
* 大学 D =[Student(name= 学生 J, school= 大学 D, age=22, score=87.0)],
* 大学 C =[Student(name= 学生 G, school= 大学 C, age=20, score=80.0), Student(name= 学生 H, school= 大学 C, age=21, score=78.0), Student(name= 学生 I, school= 大学 C, age=20, score=67.0)]}
*/
复制代码
有些时候这真的是非常有用且不便的。
然而有时候咱们往往不止于如此,如果我要统计每个学校中 20 岁年龄以上和 20 以下的学生别离有哪些学生,那么我的参数就不再是 Student::getSchool 了,而是要加上语句了。那么该如何编写呢?
// 统计每个学校中 20 岁年龄以上和 20 以下的学生别离有多少
Map<String, List<Student>> collect = students.stream().collect(Collectors.groupingBy(student -> {
if (student.getAge() > 20) {
return “20 岁以上的 ”;
}
return “20 以下的 ”;
}));
System.out.println(collect);
复制代码
如果要统计每个学校有多少 20 岁以上和 20 岁以下的学生的信息,其实也就是把 return 语句批改以下即可。
// 统计每个学校中 20 岁年龄以上和 20 以下的学生别离有多少
Map<String, List<Student>> collect = students.stream().collect(Collectors.groupingBy(student -> {
if (student.getAge() > 20) {
return student.getSchool();
}
return student.getSchool();
}));
System.out.println(collect);
复制代码
置信大家也看进去 groupingBy 中的 return 语句就是 Map 中的 key 值
2、多级分组
但其实 groupingBy()并不只是一个人,它也有兄弟姐妹
如果我想把下面的例子再革新革新,
改为:我想晓得 20 岁以上的学生在每个学校有哪些学生,20 岁以下的学生在每个学校有哪些学生。
数据结构就该当设计为 Map<String, Map<String, List<Student>>> 啦,第一级寄存 20 岁以上以下两组数据,第二级寄存以每个学校名为 key 的数据信息。
Map<String, Map<String, List<Student>>> collect = students.stream().collect(Collectors.groupingBy(Student::getSchool, Collectors.groupingBy(student -> {
if (student.getAge() > 20) {
return “20 以上的 ”;
}
return “20 岁以下的 ”;
})));
System.out.println(collect);
/**
* {大学 B ={20 岁以下的 =[Student(name= 学生 D, school= 大学 B, age=18, score=76.0),Student(name= 学生 E, school= 大学 B, age=18, score=91.0), Student(name= 学生 F, school= 大学 B, age=19, score=65.0)]},
* 大学 A ={20 岁以下的 =[Student(name= 学生 A, school= 大学 A, age=18, score=98.0), Student(name= 学生 B, school= 大学 A, age=18, score=91.0), Student(name= 学生 C, school= 大学 A, age=18, score=90.0)]},
* 大学 D ={20 以上的 =[Student(name= 学生 J, school= 大学 D, age=22, score=87.0)]},
* 大学 C ={20 以上的 =[Student(name= 学生 H, school= 大学 C, age=21, score=78.0)],20 岁以下的 =[Student(name= 学生 G, school= 大学 C, age=20, score=80.0), Student(name= 学生 I, school= 大学 C, age=20, score=67.0)]}}
*/
复制代码
这里利用的就是把一个内层 groupingBy 传递给外层 groupingBy,俗称的套娃~
外层 Map 的键就是第一级分类函数生成的值,而这个 Map 的值又是一个 Map,键是二级分类函数生成的值。
3、按子组数据进行划分
之前我的截图中,groupingBy 的重载办法中,其实对于第二个参数的限度,并非说肯定是要 groupingBy 类型的收集,更形象点说,它能够是任意的收集器~
再如果,我的例子改为:
我当初明确的想晓得每个学校 20 岁的学生的人数。
那么这个数据结构就该当改为
Map<String,Long> 或者是 Map<String,Integer> 呢?
那么在这里该如何实现呢?
Map<String, Long> collect = students.stream().collect(Collectors.groupingBy(Student::getSchool, Collectors.counting()));
System.out.println(collect);
/**
* {大学 B =3, 大学 A =3, 大学 D =1, 大学 C =3}
*/
复制代码
实际上还有许多未曾谈到的货色,这里都只是非常简单的利用,对于其中的流的执行的先后顺序,以及一些简略的原理,都没有过多的波及,大家先上手用着吧~
后记
我这里只是论述了一些比较简单的应用性操作,未谈及设计思维之类,然而要明确那种才是更值得去浏览和了解的。