文章目录
java 1.8 stream 22中应用案例
1. stream简介
java 1.8版本更新的一个亮点Stream,给开发者对集合(Collection)的操作提供了极大的便利。
特性
- stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
- stream不会改变数据源,通常情况下会产生一个新的集合或一个值
2. 案例
案例有简单到复杂
2.1 数据准备
学生 Student
public class Student {
/**
* 年龄
*/
private int age;
/**
* 姓名
*/
private String name;
/**
* 成绩
*/
private int fraction;
/**
* 性别
*/
private String sex;
/**
* 老师id
*/
private String teaId;
public Student(int age, String name, int fraction, String sex) {
this.age = age;
this.name = name;
this.fraction = fraction;
this.sex = sex;
}
public Student(int age, String name, int fraction, String sex, String teaId) {
this.age = age;
this.name = name;
this.fraction = fraction;
this.sex = sex;
this.teaId = teaId;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getFraction() {
return fraction;
}
public void setFraction(int fraction) {
this.fraction = fraction;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public void addFraction() {
this.fraction++;
}
public String getTeaId() {
return teaId;
}
public void setTeaId(String teaId) {
this.teaId = teaId;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
", fraction=" + fraction +
", sex='" + sex + '\'' +
", teaId='" + teaId + '\'' +
'}';
}
}
教师 Teacher
public class Teacher {
private String id;
private String name;
public Teacher(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
初始化数据
public static List<Student> getStudents() {
List<Student> studentList = new ArrayList<>();
studentList.add(new Student(18, "A18", 60, "man", "1"));
studentList.add(new Student(17, "A17", 50, "woman","1"));
studentList.add(new Student(19, "A19", 80, "man","3"));
studentList.add(new Student(20, "A20", 90, "woman", "2"));
studentList.add(new Student(25, "A25", 40, "man", "1"));
studentList.add(new Student(15, "A15", 92, "man", "3"));
studentList.add(new Student(16, "A16", 92, "woman", "1"));
return studentList;
}
public static List<Student> getStudents2() {
List<Student> studentList = new ArrayList<>();
studentList.add(new Student(18, "B18", 60, "man", "6"));
studentList.add(new Student(17, "B17", 50, "woman", "6"));
studentList.add(new Student(20, "B17", 50, "woman", "6"));
studentList.add(new Student(18, "B19", 80, "man", "6"));
return studentList;
}
public static List<Teacher> getTeacher() {
List<Teacher> teacherList = new ArrayList<>();
teacherList.add(new Teacher("1", "teacher"));
teacherList.add(new Teacher("3", "teacher"));
return teacherList;
}
2.2 遍历/过滤/匹配/筛选(foreach/filter/match/find)
2.2.1 遍历 foreach
test() {
List<Student> students = getStudents();
// 遍历输出方式1
students.stream().forEach(System.out::println);
System.out.println("==============================");
// 遍历输出方式2
students.stream().forEach(s -> {
// 在当前方法,可以对当前对象的引用进行操作
s.addFraction();
System.out.println(s.toString());
});
}
输出
Student{age=18, name='s18', fraction=60, sex='man'}
Student{age=17, name='A17', fraction=50, sex='man'}
Student{age=19, name='A19', fraction=80, sex='man'}
Student{age=20, name='A20', fraction=90, sex='man'}
Student{age=25, name='A25', fraction=40, sex='man'}
Student{age=15, name='A15', fraction=92, sex='man'}
==============================
Student{age=18, name='s18', fraction=61, sex='man'}
Student{age=17, name='A17', fraction=51, sex='man'}
Student{age=19, name='A19', fraction=81, sex='man'}
Student{age=20, name='A20', fraction=91, sex='man'}
Student{age=25, name='A25', fraction=41, sex='man'}
Student{age=15, name='A15', fraction=93, sex='man'}
2.2.2 过滤 filter
test() {
// 获取数据集
List<Student> students = getStudents();
// 过滤年龄>=18,并遍历输出
students.stream().filter(s -> s.getAge() >= 18).forEach(System.out::println);
}
输出
Student{age=18, name='s18', fraction=60, sex='man'}
Student{age=19, name='A19', fraction=80, sex='man'}
Student{age=20, name='A20', fraction=90, sex='man'}
Student{age=25, name='A25', fraction=40, sex='man'}
2.2.3 匹配 match
test() {
List<Student> students = getStudents();
// 判断是否存在年龄大于18
boolean anyMatch = students.stream().anyMatch(s -> s.getAge() > 18);
System.out.println(anyMatch);
// 判断所有的学生,年龄是否均大于18
boolean allMatch = students.stream().allMatch(s -> s.getAge() > 20);
System.out.println(allMatch);
// 判断当前学生,年龄是否不存在小于8
boolean noneMatch = students.stream().noneMatch(s -> s.getAge() < 8);
System.out.println(noneMatch);
}
输出
true
false
true
2.2.4 筛选 find
test() {
List<Student> students = getStudents();
// 筛选第一个
Optional<Student> first = students.stream().findFirst();
// 任意筛选一个
Optional<Student> any = students.parallelStream().findAny();
// .isPresent():判断是否有获取到结果对象
if (first.isPresent()) {
// 始终返回第一个索引数据
System.out.println(first.get());
}
if(any.isPresent()) {
// 多次测试,同一组数据,返回的结果一致
System.out.println(any.get());
}
}
输出
Student{age=18, name='s18', fraction=60, sex='man'}
Student{age=25, name='A25', fraction=40, sex='man'}
说明:
stream与parallelStream区别
- stream:顺序流,处理顺序按原List的顺序逐个执行;对数据处理先后顺序有严格要求,用顺序流,执行效率较低。
- parallelStream:并行流,对数据分批并行处理(不会存在重复消费处理的问题),可以理解为多线程处理;对数据处理无先后顺序,可以用并行流,执行效率高。
2.2.4 过滤并形成新的集合
test() {
List<Student> students = getStudents();
// 过滤年龄>=18 且 成绩 >= 80学生,并形成新的集合
// 方式1 List<Student> collect = students.stream().filter(s -> s.getAge() >= 18 && s.getFraction() >= 80).collect(Collectors.toList());
// 方式2 自定义方法
List<Student> collect = students.stream().filter(s -> {
return s.getAge() >= 18 && s.getFraction() >= 80;
}).collect(Collectors.toList());
System.out.println(collect.toString());
}
输出
[Student{age=19, name='A19', fraction=80, sex='man'}, Student{age=20, name='A20', fraction=90, sex='woman'}]
2.3 聚合(max/min/count)
2.3.1 获取最大值
test() {
List<Student> students = getStudents();
// 获取年龄最大学生
Optional<Student> maxAge = students.stream().max(Comparator.comparing(Student::getAge));
System.out.println(maxAge.get());
// 获取成绩最大的学生
Optional<Student> maxFraction = students.stream().max(Comparator.comparing(Student::getFraction));
System.out.println(maxFraction.get());
}
输出
Student{age=25, name='A25', fraction=40, sex='man'}
Student{age=15, name='A15', fraction=92, sex='man'}
2.3.2 获取最小值
test() {
List<Student> students = getStudents();
// 获取年龄最小学生
Optional<Student> minAge = students.stream().min(Comparator.comparing(Student::getAge));
System.out.println("年龄最小学生" + minAge.get());
// 获取成绩最差的学生
Optional<Student> minFraction = students.stream().min(Comparator.comparing(Student::getFraction));
System.out.println("成绩最差的学生" + minFraction.get());
}
输出
年龄最小学生Student{age=15, name='A15', fraction=92, sex='man'}
成绩最差的学生Student{age=25, name='A25', fraction=40, sex='man'}
2.3.3 过滤+汇总count
test() {
List<Student> students = getStudents();
// 总条数
long count = students.stream().count();
System.out.println("总条数:" + count);
// 汇总年龄>18 且 成绩>80的学生个数
long count1 = students.stream().filter(s -> s.getAge() > 18 && s.getFraction() > 80).count();
System.out.println("年龄>18 且 成绩>80的学生个数:" + count1);
}
输出
总条数:7
年龄>18 且 成绩>80的学生个数:1
2.4 映射(map/flatMap)
映射:可以将一个流的元素按照一定的映射规则映射到另一个流中
map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
2.4.1 map
test() {
List<Student> students = getStudents();
// 映射出学生的名字,并打印输出
students.stream().map(Student::getName).forEach(System.out::println);
// 每位学生的年纪+1,改变原集合中的数据
students.stream().map(stu -> {
stu.setAge(stu.getAge() + 1);
return stu;
}).forEach(System.out::println);
}
输出
[s18, A17, A19, A20, A25, A15, A16]
Student{age=19, name='s18', fraction=60, sex='man'}
Student{age=18, name='A17', fraction=50, sex='woman'}
Student{age=20, name='A19', fraction=80, sex='man'}
Student{age=21, name='A20', fraction=90, sex='woman'}
Student{age=26, name='A25', fraction=40, sex='man'}
Student{age=16, name='A15', fraction=92, sex='man'}
Student{age=17, name='A16', fraction=92, sex='woman'}
2.4.2 flatMap
看一下flatMap方法定义:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
参数是一个Function函数式接口,提供T到Stram的转换。其实参考方法实现,flatMap就是将Function转化后的Stram合并成一个Stream。
2.4.2.1 合并 + 获取交集
test() {
List<Student> students1 = getStudents();
List<Student> students2 = getStudents2();
Student student = new Student(99, "99", 60, "man", "99");
students1.add(student);
students2.add(student);
// 1.将两个流合并在一起
// 方式1 flatMap中的返回的参数必须是stream流
List<Student> collect = Stream.of(students1, students2).flatMap(Collection::stream).collect(Collectors.toList());
// 方式2
List<Student> collect1 = Stream.of(students1, students2).flatMap(list -> list.stream()).collect(Collectors.toList());
System.out.println(collect.size());
System.out.println(collect1.size());
// 2 取交集
List<Student> collect2 = students1.stream().filter(s -> students2.contains(s)).collect(Collectors.toList());
System.out.println(collect2.toString());
}
输出
并集:[Student{age=18, name='A18', fraction=60, sex='man', teaId='1'}, Student{age=17, name='A17', fraction=50, sex='woman', teaId='1'}, Student{age=19, name='A19', fraction=80, sex='man', teaId='3'}, Student{age=20, name='A20', fraction=90, sex='woman', teaId='2'}, Student{age=25, name='A25', fraction=40, sex='man', teaId='1'}, Student{age=15, name='A15', fraction=92, sex='man', teaId='3'}, Student{age=16, name='A16', fraction=92, sex='woman', teaId='1'}, Student{age=99, name='99', fraction=60, sex='man', teaId='99'}, Student{age=18, name='B18', fraction=60, sex='man', teaId='6'}, Student{age=17, name='B17', fraction=50, sex='woman', teaId='6'}, Student{age=20, name='B17', fraction=50, sex='woman', teaId='6'}, Student{age=18, name='B19', fraction=80, sex='man', teaId='6'}, Student{age=99, name='99', fraction=60, sex='man', teaId='99'}]
并集:[Student{age=18, name='A18', fraction=60, sex='man', teaId='1'}, Student{age=17, name='A17', fraction=50, sex='woman', teaId='1'}, Student{age=19, name='A19', fraction=80, sex='man', teaId='3'}, Student{age=20, name='A20', fraction=90, sex='woman', teaId='2'}, Student{age=25, name='A25', fraction=40, sex='man', teaId='1'}, Student{age=15, name='A15', fraction=92, sex='man', teaId='3'}, Student{age=16, name='A16', fraction=92, sex='woman', teaId='1'}, Student{age=99, name='99', fraction=60, sex='man', teaId='99'}, Student{age=18, name='B18', fraction=60, sex='man', teaId='6'}, Student{age=17, name='B17', fraction=50, sex='woman', teaId='6'}, Student{age=20, name='B17', fraction=50, sex='woman', teaId='6'}, Student{age=18, name='B19', fraction=80, sex='man', teaId='6'}, Student{age=99, name='99', fraction=60, sex='man', teaId='99'}]
交集:[Student{age=99, name='99', fraction=60, sex='man', teaId='99'}]
2.4.2.2 多集合获取关联数据
一批教师:teacher
一班学生:students1
二班学生:students2
复杂,获取指定教师下的学生, 转Map<teaId, List>
test() {
List<Student> students1 = getStudents();
List<Student> students2 = getStudents2();
List<Teacher> teachers = getTeacher();
Student student = new Student(99, "99", 60, "man", "99");
students1.add(student);
students2.add(student);
Map<String, List<Student>> collect3 = teachers.stream()
// 难理解的点:合并所有学生,并过滤筛选出这批教师下的学生
.flatMap(tea -> Stream.of(students1, students2)
.flatMap(Collection::stream)
.filter(stu -> stu.getTeaId().equals(tea.getId()))
)
// 最终获取这批教师下的学生流集合,分组
.collect(Collectors.groupingBy(Student::getTeaId));
for (String key: collect3.keySet()) {
System.out.println(collect3.get(key));
}
}
输出
[Student{age=18, name='A18', fraction=60, sex='man', teaId='1'}, Student{age=17, name='A17', fraction=50, sex='woman', teaId='1'}, Student{age=25, name='A25', fraction=40, sex='man', teaId='1'}, Student{age=16, name='A16', fraction=92, sex='woman', teaId='1'}]
[Student{age=19, name='A19', fraction=80, sex='man', teaId='3'}, Student{age=15, name='A15', fraction=92, sex='man', teaId='3'}]
2.4.2.3 flatMapToInt
test() {
List<List<Student>> studentList = new ArrayList<>();
List<Student> students = getStudents();
List<Student> students2 = getStudents2();
studentList.add(students);
studentList.add(students2);
// 收集所有学生的成绩
IntStream intStream =
studentList.stream()
.flatMapToInt(stus ->
stus.stream()
.mapToInt(Student::getFraction));
// 遍历输出,并计算平均估值
OptionalDouble average = intStream.peek(System.out::println).average();
System.out.println("average: " + average.getAsDouble());
}
输出
...
60
50
50
80
average: 67.63636363636364
2.5 归约(reduce)
归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
2.5.1 简单案例
test() {
List<Integer> list = Arrays.asList(1, 3, 5, 7, 9, 11);
// 求和方式1
Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);
// 求和方式2
Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
// 求和方式3
Integer sum3 = list.stream().reduce(0, Integer::sum);
// 求乘积
Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
// 求最大值方式1
Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
// 求最大值写法2
Integer max2 = list.stream().reduce(1, Integer::max);
System.out.println("list求和:" + sum.get() + "," + sum2.get() + "," + sum3);
System.out.println("list求积:" + product.get());
System.out.println("list求和:" + max.get() + "," + max2);
}
输出
list求和:36,36,36
list求积:10395
list求和:11,11
2.5.2 对象案例
test() {
List<Student> students = getStudents();
// 求所有学生成绩和
// 方式1
Optional<Integer> sum1 = students.stream().map(Student::getFraction).reduce(Integer::sum);
// 方式2
Optional<Integer> sum2 = students.stream().map(Student::getFraction).reduce((sum, item) -> sum += item);
// 其他方式
Integer sum = students.stream().mapToInt(Student::getFraction).sum();
System.out.println("求和:" + sum1.get() + "," + sum2.get() + "," + sum);
// 求学生中最高成绩
Optional<Integer> max1 = students.stream().map(Student::getFraction).reduce(Integer::max);
// 其他方式
OptionalInt max2 = students.stream().mapToInt(Student::getFraction).max();
System.out.println("最大值:" + max1.get() + "," + max2.getAsInt());
}
输出
求和:504,504,504
最大值:92,92
2.6 收集(collect)
收集(collect),可以说是内容最繁多、功能最丰富的部分、在开发过程中最常用的。从字面上去理解,就是把一个流收集起来,最终将流转换成新的集合List<?>、Map<?, ?>、Set<?>。
2.6.1 归集(toList/toSet/toMap)
test() {
List<Student> students = getStudents();
// 获取所有学生的名字,存储到List<String>集合中
List<String> nameList = students.stream().map(Student::getName).collect(Collectors.toList());
// 流转换->Map<key, Student> key:学生姓名
Map<String, Student> mapStudent = students.stream().collect(Collectors.toMap(Student::getName, Function.identity()));
// 流转换->Set<Student>
Set<Student> setStudent = students.stream().collect(Collectors.toSet());
System.out.println("toList: " + nameList);
System.out.println("toMap: " + mapStudent);
System.out.println("toSet: " + setStudent);
}
输出
toList: [A18, A17, A19, A20, A25, A15, A16]
toMap: {A25=Student{age=25, name='A25', fraction=40, sex='man', teaId='1'}, A15=Student{age=15, name='A15', fraction=92, sex='man', teaId='3'}, A16=Student{age=16, name='A16', fraction=92, sex='woman', teaId='1'}, A17=Student{age=17, name='A17', fraction=50, sex='woman', teaId='1'}, A18=Student{age=18, name='A18', fraction=60, sex='man', teaId='1'}, A19=Student{age=19, name='A19', fraction=80, sex='man', teaId='3'}, A20=Student{age=20, name='A20', fraction=90, sex='woman', teaId='2'}}
toSet: [Student{age=18, name='A18', fraction=60, sex='man', teaId='1'}, Student{age=25, name='A25', fraction=40, sex='man', teaId='1'}, Student{age=19, name='A19', fraction=80, sex='man', teaId='3'}, Student{age=17, name='A17', fraction=50, sex='woman', teaId='1'}, Student{age=16, name='A16', fraction=92, sex='woman', teaId='1'}, Student{age=20, name='A20', fraction=90, sex='woman', teaId='2'}, Student{age=15, name='A15', fraction=92, sex='man', teaId='3'}]
2.6.1 统计(count/averaging)
提供了一系列用于数据统计的静态方法:
- 计数:count
- 平均值:averagingInt、averagingLong、averagingDouble
- 最值:maxBy、minBy
- 求和:summingInt、summingLong、summingDouble
统计以上所有:summarizingInt、summarizingLong、summarizingDouble
test() {
List<Student> students = getStudents();
// 求总数
Long count = students.stream().collect(Collectors.counting());
// 求平均成绩
Double avg = students.stream().collect(Collectors.averagingDouble(Student::getFraction));
// 求最高成绩
Optional<Integer> max = students.stream().map(Student::getFraction).collect(Collectors.maxBy(Integer::compareTo));
// 求最低成绩
Optional<Integer> min = students.stream().map(Student::getFraction).collect(Collectors.minBy(Integer::compareTo));
// 成绩求和
Double sum = students.stream().collect(Collectors.summingDouble(s -> s.getFraction()));
// 一次性统计
DoubleSummaryStatistics collect = students.stream().collect(Collectors.summarizingDouble(s -> s.getFraction()));
System.out.println("总数: " + count);
System.out.println("平均成绩: " + avg);
System.out.println("最高成绩: " + max);
System.out.println("最低成绩: " + min);
System.out.println("成绩求和: " + sum);
System.out.println("学生成绩一次性统计: " + collect);
}
输出
总数: 7
平均成绩: 72.0
最高成绩: Optional[92]
最低成绩: Optional[40]
成绩求和: 504.0
学生成绩一次性统计: DoubleSummaryStatistics{count=7, sum=504.000000, min=40.000000, average=72.000000, max=92.000000}
2.6.3 分组 (partitioningBy/groupingBy)
分区:将stream按条件分为两个Map<Boolean, Object>,比如学生按成绩是否高于80分为两部分。
分组:将集合分为多个Map,比如学生按性别分组;有单级分组和多级分组。
test() {
List<Student> students = getStudents();
// 分组:按性别分组
Map<String, List<Student>> sexStudents = students.stream().collect(Collectors.groupingBy(Student::getSex));
// 分区:按成绩[0, 80), [80,100]分区
Map<Boolean, List<Student>> collect = students.stream().collect(Collectors.partitioningBy(stu -> stu.getFraction() >= 80));
// 二级分组:先按性别分组,在按成绩分区
Map<String, Map<Boolean, List<Student>>> collect1 = students.stream().collect(Collectors.groupingBy(Student::getSex, Collectors.partitioningBy(stu -> stu.getFraction() >= 80)));
System.out.println("按性别分组:" + sexStudents);
System.out.println("按成绩分区:" + collect);
System.out.println("按性别分组,再按成绩分区:" + collect1);
}
输出
按性别分组:{woman=[Student{age=17, name='A17', fraction=50, sex='woman', teaId='1'}, Student{age=20, name='A20', fraction=90, sex='woman', teaId='2'}, Student{age=16, name='A16', fraction=92, sex='woman', teaId='1'}], man=[Student{age=18, name='A18', fraction=60, sex='man', teaId='1'}, Student{age=19, name='A19', fraction=80, sex='man', teaId='3'}, Student{age=25, name='A25', fraction=40, sex='man', teaId='1'}, Student{age=15, name='A15', fraction=92, sex='man', teaId='3'}]}
按成绩分区:{false=[Student{age=18, name='A18', fraction=60, sex='man', teaId='1'}, Student{age=17, name='A17', fraction=50, sex='woman', teaId='1'}, Student{age=25, name='A25', fraction=40, sex='man', teaId='1'}], true=[Student{age=19, name='A19', fraction=80, sex='man', teaId='3'}, Student{age=20, name='A20', fraction=90, sex='woman', teaId='2'}, Student{age=15, name='A15', fraction=92, sex='man', teaId='3'}, Student{age=16, name='A16', fraction=92, sex='woman', teaId='1'}]}
按性别分组,再按成绩分区:{woman={false=[Student{age=17, name='A17', fraction=50, sex='woman', teaId='1'}], true=[Student{age=20, name='A20', fraction=90, sex='woman', teaId='2'}, Student{age=16, name='A16', fraction=92, sex='woman', teaId='1'}]}, man={false=[Student{age=18, name='A18', fraction=60, sex='man', teaId='1'}, Student{age=25, name='A25', fraction=40, sex='man', teaId='1'}], true=[Student{age=19, name='A19', fraction=80, sex='man', teaId='3'}, Student{age=15, name='A15', fraction=92, sex='man', teaId='3'}]}}
2.6.4 接合 (joining)
joining可以将stream中的元素用特定的连接符,没有的话,则直接连接成一个字符串。
test() {
List<Student> students = getStudents();
String collect = students.stream().map(stu -> stu.getName()).collect(Collectors.joining());
String collect1 = students.stream().map(stu -> stu.getName()).collect(Collectors.joining(","));
System.out.println(collect);
System.out.println("学生姓名:" + collect1);
}
输出
A18A17A19A20A25A15A16
学生姓名:A18,A17,A19,A20,A25,A15,A16
2.7 排序(sorted)
sorted,中间操作。有两种排序:
- sorted():自然排序,流中元素需实现Comparable接口
- sorted(Comparator com):Comparator排序器自定义排序
2.7.1 自然排序
test() {
List<Student> students = getStudents();
// 按成绩从小到大排序
String collect = students.stream().sorted(Comparator.comparing(Student::getFraction)).map(stu -> String.valueOf(stu.getFraction())).collect(Collectors.joining(","));
// 按成绩成大到小排序
String collect1 = students.stream().sorted(Comparator.comparing(Student::getFraction).reversed()).map(stu -> String.valueOf(stu.getFraction())).collect(Collectors.joining(","));
System.out.println("按成绩从小到大:" + collect);
System.out.println("按成绩从大到小:" + collect1);
}
输出
按成绩从小到大:40,50,60,80,90,92,92
按成绩从大到小:92,92,90,80,60,50,40
2.7.2 自定义排序
test() {
List<Student> students = getStudents();
// 先按年龄从小到大排序,再按成绩从大到小排序
List<String> collect = students.stream().sorted(Comparator.comparing(Student::getAge).thenComparing(Comparator.comparing(Student::getFraction).reversed()))
.map(stu -> "年龄:" + stu.getAge() + " 成绩:" + stu.getFraction()).collect(Collectors.toList());
// 自定义
List<String> collect1 = students.stream().sorted((s1, s2) -> {
if (s1.getAge() == s2.getAge()) {
// 成绩降序
return s2.getFraction() - s1.getFraction();
} else {
// 年龄升序
return s1.getAge() - s2.getAge();
}
}).map(stu -> "年龄:" + stu.getAge() + " 成绩:" + stu.getFraction()).collect(Collectors.toList());
System.out.println(collect);
System.out.println(collect1);
}
输出
[年龄:15 成绩:92, 年龄:16 成绩:92, 年龄:17 成绩:50, 年龄:18 成绩:60, 年龄:19 成绩:80, 年龄:20 成绩:90, 年龄:25 成绩:40]
[年龄:15 成绩:92, 年龄:16 成绩:92, 年龄:17 成绩:50, 年龄:18 成绩:60, 年龄:19 成绩:80, 年龄:20 成绩:90, 年龄:25 成绩:40]
2.8 提取/合并
流也可以进行合并、去重、限制、跳过等操作。
- concat:合并留。
- distinct:去重,去重的判断方法是对象自带equal。
- limit:限制从流中获得前n个数据。
- skip:跳过。
2.8.1 合并 concat
test() {
String[] arr1 = { "1", "2", "3", "4" };
String[] arr2 = { "4", "5", "6", "7" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
// concat 合并
List<String> collect = Stream.concat(stream1, stream2).collect(Collectors.toList());
System.out.println("合并:" + collect);
}
输出
合并:[1, 2, 3, 4, 4, 5, 6, 7]
2.8.2 去重 distinct
test() {
String[] arr1 = { "1", "2", "3", "4" };
String[] arr2 = { "4", "5", "6", "7" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
// distinct去重
List<String> collect1 = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
System.out.println("去重:" + collect1);
}
输出
去重:[1, 2, 3, 4, 5, 6, 7]
2.8.3 限制 limit
test() {
String[] arr1 = { "1", "2", "3", "4" };
String[] arr2 = { "4", "5", "6", "7" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
// limit限制 取前3位
List<String> collect2 = Stream.concat(stream1, stream2).limit(3).collect(Collectors.toList());
System.out.println("限制:" + collect2);
}
输出
限制:[1, 2, 3]
2.8.4 跳过 skip
test() {
String[] arr1 = { "1", "2", "3", "4" };
String[] arr2 = { "4", "5", "6", "7" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
// skip:跳过 前4位
List<String> collect3 = Stream.concat(stream1, stream2).skip(4);
}
输出
跳过:[4, 5, 6, 7]
2.9 总结
- 程序运行中,流Stream只能使用一次,使用后会默认关闭,不能重复使用;重复使用会报错,信息如下:
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.spliterator(AbstractPipeline.java:343)
at java.util.stream.Stream.concat(Stream.java:1080)
- 在开发过程中,建议使用Stream.of(list),解决list为null的问题;使用list.stream()之前需要判断list是否为null,避免报错空指针异常(java.lang.NullPointerException)。
- 动起手来敲代码验证,会有不一样的收获。
- stream流遍历获取的对象是原对象
- stream特性思维导图: