前言
在后面的文章中其实大家也曾经看到我应用过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}
*/
复制代码
实际上还有许多未曾谈到的货色,这里都只是非常简单的利用,对于其中的流的执行的先后顺序,以及一些简略的原理,都没有过多的波及,大家先上手用着吧~
后记
我这里只是论述了一些比较简单的应用性操作,未谈及设计思维之类,然而要明确那种才是更值得去浏览和了解的。