- 一、JDK1.8中的函数式接口和流
- 二、Stream(流)
- 三、集合流加强版
- 4.1.2.1. 集合流的介绍
- 4.1.2.2. 使用流式编程的步骤
- 4.1.2.3. 读取数据源
- 4.1.2.4. 最终操作
- collect
// 将流中的元素聚合到一起,最常见的做法就是将元素都搜集起来,存入一个集合里面。 - reduce —合并操作
- count
- forEach
- max&min
// 获取流中的元素的最大值、最小值 - anyMatch&allMatch&noneMatch
// allMatch: 只有当流中所有的元素,都匹配指定的规则,才会返回 true
// anyMatch: 只要流中有任意的数据,满足指定的规则,都会返回 true
// noneMatch: 只有当流中的所有的元素,都不满足指定的规则,才会返回true - find
// findFirst: 从流中获取一个元素(一般情况下,是获取的开头的元素)
// findAny: 从流中获取一个元素(一般情况下,是获取的开头的元素)
// 这两个方法,绝大部分情况下,是完全相同的,但是在多线程的环境下,findAny和find返回的结果可能不一样。 - 异常演示
// 最终操作会关闭流,此时再使用这个流进行其他的中间或最终操作都会有问题
- collect
- 4.1.2.5. 中间操作
- filter
// 条件过滤,仅保留集合中满足条件的数据 - distinct
// 去除集合中重复的元素,无参的方法,去重的规则与HashSet相同:
// 1. 先比较hashCode,如果不相同,视为不重复的元素
// 2. 如果hashCode相同,再比较equals,如果不相同,视为不重复的元素
// 3. 如果hashCode相同,equals比较也相同,视为重复的元素 - sorted
// 对流中的元素进行排序
// 无参: 使用元素对应的类实现的Comparable接口的实现,进行元素的大小比较
// 有参: 使用参数Comparator接口的实现,按照指定的规则进行元素的大小比较 - limit&skip
// limit: 限制,截取流中指定数量的元素
// skip: 跳过,跳过流中的指定数量的元素 - map
// 元素映射,将流中的元素按照指定的规则进行一对一的替换 - mapToInt
// 将流中的元素映射成int类型的数据,得到的结果是IntStream对象,方便进行数据的统计 - flatMap
String[] strs = { “hello”, “world” };
- filter
- 4.1.2.6. 实战案例
一、JDK1.8中的函数式接口和流
在jdk1.8中有一个包 java.util.function,存放的都是函数式接口
这么多函数式接口,大致分为这么几类,如下图:
代码演示一下用法,有个印象:
//R apply(T t);函数型接口,一个参数,一个返回值 Function System.out.println(function.apply(“abcd”)); //boolean test(T t);断定型接口,一个参数,返回boolean Predicate System.out.println(predicate.test(“a”)); // void accept(T t);消费型接口,一个参数,没有返回值 Consumer System.out.println(t); }; consumer.accept(“javaXXXX”); //T get(); 供给型接口,无参数,有返回值 Supplier System.out.println(supplier.get()); |
---|
package com.qfedu.day12;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @Author laoyan
* @Description TODO
* @Date 2022/3/16 9:55
* @Version 1.0
*
* 请按照给出数据,找出
* * 偶数ID
* * 年龄大于24
* * 用户名转为大写
* * 用户名字母倒排序
* * 只输出2个
* //首先我们需要一个数据源 集合或者数组
* List<User> list =new ArrayList<User>();
* list.add(new User(11,"zhangsan",25));
* list.add(new User(12,"lisi",25));
* list.add(new User(13,"wangwu",35));
* list.add(new User(14,"zhaoliu",15));
* list.add(new User(15,"tanxin",38));
* list.add(new User(16,"wang",45));
*/
public class TestStream {
public static void main(String[] args) {
List<User> list =new ArrayList<User>();
list.add(new User(11,"zhangsan",25));
list.add(new User(12,"lisi",25));
list.add(new User(13,"wangwu",35));
list.add(new User(14,"zhaoliu",15));
list.add(new User(15,"tanxin",38));
list.add(new User(16,"wang",45));
/**
* 使用stream流
*/
Stream<User> stream = list.stream();
/*
可以通过工具类获取数组的Stream流
String[] arr = {"张三"};
Stream<String> stream1 = Arrays.stream(arr);
*/
/**
* 过滤 filter
* 返回true 表示保留,返回false 代表过滤
* map
* 将集合中的元素一个个传递进来,然后经过处理再返回回去
* limit
* 限定输出的个数
* foreach
* 对集合中的元素循环遍历。
* sorted
* 排序,里面可以放入一个比较器
* collect
* 从一个集合中获取一个Stream流,通过流进行数据的处理,处理完之后,我们实际的集合里面的元素是不会发生改变的。
* 我们如果想将处理完的流Stream对象变为list 集合,需要使用到 collect
* 收集的意思: 可以收集成list , set
* 该方法是流对象的方法,不能在forEach 方法后调用。
*/
List<User> userList = stream.filter(user -> {
if (user.getId() % 2 == 0) {
return true;
}
return false;
}).filter(user -> {
if (user.getAge() > 24) {
return true;
}
return false;
}).map(user -> {
String daXie = user.getName().toUpperCase();
StringBuffer stringBuffer = new StringBuffer(daXie);
System.out.println(stringBuffer.toString());
user.setName(stringBuffer.reverse().toString());
return user;
}).sorted((u1, u2) ->
//u1.getAge() - u2.getAge()
u1.getName().compareTo(u2.getName())
).limit(2).collect(Collectors.toList());/*forEach(user->{
System.out.println(user);
})*/
System.out.println(userList);
}
}
二、Stream(流)
流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算!”
Stream 自己不会存储元素,Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
一般开发的套路是:创建一个Stream:一个数据源(数组、集合)—>中间操作:一个中间操作,处理数据源数据—>终止操作:一个终止操作,执行中间操作链,产生结果.
案例:
/* 请按照给出数据,找出 偶数ID 年龄大于24 用户名转为大写 用户名字母倒排序 只输出一个 用户名字 */ |
---|
举例:
请按照给出数据,找出
* 偶数ID
* 年龄大于24
* 用户名转为大写
* 用户名字母倒排序
* 只输出2个
//首先我们需要一个数据源 集合或者数组
List<User> list =new ArrayList<User>();
list.add(new User(11,"zhangsan",25));
list.add(new User(12,"lisi",25));
list.add(new User(13,"wangwu",35));
list.add(new User(14,"zhaoliu",15));
list.add(new User(15,"tanxin",38));
list.add(new User(16,"wang",45));
package com.qfedu.stream;
public class User {
private int id;
private String name;
private int age;
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.qfedu.stream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
/*
* 请按照给出数据,找出
* 偶数ID
* 年龄大于24
* 用户名转为大写
* 用户名字母倒排序
* 只输出2个
*
* Stream 流 很重要,因为现在很多地方都这么写了,一定要重视一下
* 常用的方法:
* filter 接收的是断定型接口
* map 接收的是函数型接口
* sorted 接收一个比较器
* forEach 接收一个消费性接口
* limit 表示显示多少行
*
*/
public class TestStreamMain {
public static void main(String[] args) {
//首先我们需要一个数据源 集合或者数组
List<User> list =new ArrayList<User>();
list.add(new User(11,"zhangsan",25));
list.add(new User(12,"lisi",25));
list.add(new User(13,"wangwu",35));
list.add(new User(14,"zhaoliu",15));
list.add(new User(15,"tanxin",38));
list.add(new User(16,"wang",45));
Stream<User> stream = list.stream();
// filter 过滤 里面需要接收一个断定型接口
// 因为是对list集合进行遍历,所以p指的就是一个个的user
stream.filter( p-> {
if(p.getId()%2==0){
return true;
}
return false;
// filter 可以用来过滤数据
}).filter( p->{
if(p.getAge()>24){
return true;
}
return false;
// map 可以用来修改数据
}).map( p->{
p.setName(p.getName().toUpperCase());
return p;
// sorted 对数据按照一定规则排序,里面使用到了比较器这样一个接口
}).sorted((user1, user2) -> {
return user2.getName().compareTo(user1.getName());
}).limit(1)
.forEach(System.out::println); //等同于如下操作
/*forEach((t)->{
System.out.println(t);
})*/;
}
}
三、集合流加强版
4.1.2.1. 集合流的介绍
集合流,使用类Stream来表示,是Java8的新特性之一。使用集合流可以对集合的操作进行功能增强,流并不是集合的元素,也不是一种数据结构,不负责数据的存储操作。更像是迭代器,可以实现对集合中的元素进行单向不循环的遍历。
在我们对集合中的元素进行操作的时候,有时候需要使用到其他操作的结果。
例如: 在一个存储了所有的考试成绩的集合中,我需要计算及格部分的最低成绩是多少。
对于这样的需求,我需要先提取出来集合中满足条件的元素(及格的成绩),然后再计算最低成绩。
在这个过程中,集合的流式编程可以大幅度的简化代码的数量。将数据源中的数据,读取到一个流中,可以对这个流中的数据进行操作(删除、过滤、映射…)。每次的操作结果也是一个流对象,可以对这个流再进行其他的操作。
4.1.2.2. 使用流式编程的步骤
使用流式编程来操作集合中的元素,可以分为三步:
- 读取数据源的数据,构建流对象
- 对流中的数据进行各种处理
- 对处理之后的流中的数据,进行聚合的操作
其中,第二部分的操作,我们可以对流中的数据进行各种各样的处理。每一次的处理结果得到的也是一个流对象本身。可以使用这个返回值(流对象)继续进行其他的处理操作。因此这样的称为 — 中间操作。
第三部分的操作,我们对流中的数据进行聚合,得到的不再是一个流对象,而是具体的数据了。这样的操作称为 — 最终操作。
中间操作和最终操作的方法,基本都是以一个或多个函数式接口为参数的,因此需要熟练掌握Lambda表达式,才能体会到流式编程的强大之处。
4.1.2.3. 读取数据源
顾名思义,这一步的操作就是将一个集合作为数据源,读取其中的元素,构建一个Stream对象。此时Stream对象中将包含集合中的所有元素,我们可以使用流式的操作,对元素进行各种处理。
通常情况下,我们会使用Collection集合,或者数组作为数据源。
注意: Map是不能作为数据源的!
// 实例化一个集合对象,并进行元素的添加
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
// 1. 通过Collection接口中的stream()方法获取数据源为Collection的流
Stream<Integer> stream = list.stream();
// 2. 通过Collection接口的parallelStream()方法获取数据源为Collection的流
Stream<Integer> stream = list.parallelStream();
// 3. 通过Arrays工具类中的stream()方法获取数据源为数组的流
IntStream stream = Arrays.stream(array);
关于 stream() 和 parallelStream()
他们都是Collection集合获取数据源的方法,不同点在于stream()方法获取的数据源是串行的,parallelStream()获取的数据源是并行的。parallelStream()内部集成了多个线程对流中的数据进行操作,效率更高。
4.1.2.4. 最终操作
我们先来学习最终操作,是因为即便我们知道了如何对数据源中的元素进行一些中间操作,例如去重、排序、截取、过滤等操作,我们最终得到的还是一个Stream对象,无法直观的看到这个Stream种包含什么元素,也就不知道我们的操作是否是成功的。所以我们先学习最终操作。知道了如何对流中的数据进行提取和统计的计算之后,再来学习中间操作。
注意事项说在前:
最终操作,之所以叫最终操作。除了这个操作得到的不再是流对象Stream,我们没有办法继续流式的操作下去之外。还有一个很重要的因素: 最终操作会关闭这个流对象本身,我们对一个已经关闭的流进行元素的处理,会有异常!
因此,我们接下来的最终操作的学习过程中切记,千万不要对一个已经关闭的流进行元素的处理。
collect
// 将流中的元素聚合到一起,最常见的做法就是将元素都搜集起来,存入一个集合里面。
// 实例化一个集合对象,并进行元素的添加
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
// 1.1. 转成 List
List<Integer> result1 = list.stream().collect(Collectors.toList());
System.out.println(result1);
// 1.2. 转成 Set
Set<Integer> result2 = list.stream().collect(Collectors.toSet());
System.out.println(result2);
// 1.3. 转成 Map,提供两个函数式接口的实现,分别实现键的生成规则和值的生成规则
Map<Integer, Integer> result3 = list.stream().collect(Collectors.toMap(ele -> ele / 10, ele -> ele));
System.out.println(result3);
reduce —合并操作
// 实例化一个集合对象,并进行元素的添加
ArrayList list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
// 将流的元素,逐一带入到这个方法中,进行运算
// 最终的运算结果,得到的其实是一个 Optional 类型,需要使用 get() 获取到里面的数据
int result4 = list.stream().reduce((e1, e2) -> e1 + e2).get();
System.out.println(result4);
count
// 实例化一个集合对象,并进行元素的添加
ArrayList list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
// 统计流中的元素的数量
long result5 = list.stream().count();
System.out.println(result5);
forEach
// 实例化一个集合对象,并进行元素的添加
ArrayList list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
// 迭代、遍历流中的元素
list.stream().forEach(System.out::println);
max&min
// 获取流中的元素的最大值、最小值
// 实例化一个集合对象,并进行元素的添加
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
// 获取最大值
Integer result6 = list.stream().max(Integer::compareTo).get();
System.out.println("max is : " + result6);
// 获取最小值
Integer result7 = list.stream().min(Integer::compareTo).get();
System.out.println("min is : " + result7);
anyMatch&allMatch&noneMatch
// allMatch: 只有当流中所有的元素,都匹配指定的规则,才会返回 true
// anyMatch: 只要流中有任意的数据,满足指定的规则,都会返回 true
// noneMatch: 只有当流中的所有的元素,都不满足指定的规则,才会返回true
// 实例化一个集合对象,并进行元素的添加
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
// 判断流中是否所有的元素都大于 50
boolean result8 = list.stream().allMatch(ele -> ele > 50);
System.out.println(result8);
// 判断流中是否有大于 50 的数据
boolean result9 = list.stream().anyMatch(ele -> ele > 50);
System.out.println(result9);
// 判断流中是否没有奇数
boolean result10 = list.stream().noneMatch(ele -> ele % 2 != 0);
System.out.println(result10);
find
// findFirst: 从流中获取一个元素(一般情况下,是获取的开头的元素)
// findAny: 从流中获取一个元素(一般情况下,是获取的开头的元素)
// 这两个方法,绝大部分情况下,是完全相同的,但是在多线程的环境下,findAny和find返回的结果可能不一样。
// 实例化一个集合对象,并进行元素的添加
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
Integer result11 = list.parallelStream().findFirst().get();
System.out.println(result11);
Integer result12 = list.parallelStream().findAny().get();
System.out.println(result12);
List<String> lst1 = Arrays.asList("Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");
List<String> lst2 = Arrays.asList("Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");
Optional<String> findFirst = lst1.parallelStream().filter(s -> s.startsWith("D")).findFirst();
Optional<String> fidnAny = lst2.parallelStream().filter(s -> s.startsWith("J")).findAny();
System.out.println(findFirst.get()); //总是打印出David
System.out.println(fidnAny.get()); //会随机地打印出Jack/Jill/Julia
异常演示
// 最终操作会关闭流,此时再使用这个流进行其他的中间或最终操作都会有问题
// 实例化一个集合对象,并进行元素的添加
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
// 获取流对象
Stream<Integer> stream = list.stream();
// 已经进行了最终操作,流对象已经关闭
long count = stream.count();
// 使用已经关闭的流,继续进行其他的操作,异常
stream.forEach(System.out::println);
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at com.qf.cstream.FinalOperationDemo.main(FinalOperationDemo.java:78)
4.1.2.5. 中间操作
将数据从数据源中读取到流中,中间操作,就是对流中的数据进行各种各样的操作、处理。中间操作可以连续操作,每一个操作的返回值都是一个Stream对象,可以继续进行其他的操作。直到最终操作。
filter
// 条件过滤,仅保留集合中满足条件的数据
// 实例化一个集合对象,并进行元素的添加
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
list.stream().filter(ele -> ele.length() > 5).forEach(System.out::println);
distinct
// 去除集合中重复的元素,无参的方法,去重的规则与HashSet相同:
// 1. 先比较hashCode,如果不相同,视为不重复的元素
// 2. 如果hashCode相同,再比较equals,如果不相同,视为不重复的元素
// 3. 如果hashCode相同,equals比较也相同,视为重复的元素
// 实例化一个集合对象,并进行元素的添加
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
list.stream().distinct().forEach(System.out::println);
sorted
// 对流中的元素进行排序
// 无参: 使用元素对应的类实现的Comparable接口的实现,进行元素的大小比较
// 有参: 使用参数Comparator接口的实现,按照指定的规则进行元素的大小比较
// 实例化一个集合对象,并进行元素的添加
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
// 按照流中的元素对应的类,实现的 Comparable 接口中的方法实现排序
list.stream().sorted().forEach(System.out::println);
// 将流中的数据,按照指定的规则进行排序
list.stream().sorted((e1, e2) -> e1.length() - e2.length()).forEach(System.out::println);
limit&skip
// limit: 限制,截取流中指定数量的元素
// skip: 跳过,跳过流中的指定数量的元素
// 实例化一个集合对象,并进行元素的添加
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
// 跳过开头的指定的元素
list.stream().skip(3).forEach(System.out::println);
// 截取指定数量的元素
list.stream().limit(3).forEach(System.out::println);
// 配合使用,截取部分
list.stream().skip(5).limit(2).forEach(System.out::println);
map
// 元素映射,将流中的元素按照指定的规则进行一对一的替换
// 实例化一个集合对象,并进行元素的添加
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 20, 5, 18, 9, 21, 22, 21, 20, 92, 9, 20);
list.stream().map(ele -> ele + ".txt").forEach(System.out::println);
mapToInt
// 将流中的元素映射成int类型的数据,得到的结果是IntStream对象,方便进行数据的统计
double avg = list.stream().mapToInt(Person::getAge).average().getAsDouble();
System.out.println(avg);
flatMap
String[] strs = { “hello”, “world” };
// 这里是一个直接映射,将流中的元素(字符串),替换成了由这个字符串分解出来的字符数组。
// 在映射结束后,流中的数据依然是两个,分别是两个字符数组
Arrays.stream(strs).map(String::toCharArray).forEach(ele -> System.out.println(Arrays.toString(ele)));
// flatMap: 扁平化映射
// 常用于map直接映射完成后,流中的数据是一个个的容器,而我们需要对容器中的元素进行处理
// 此时,可以使用扁平化映射,将容器中的元素直接存入流中
Arrays.stream(strs).map(str -> str.split(""))
.flatMap(Arrays::stream)
.distinct()
.forEach(System.out::println);
4.1.2.6. 实战案例
需求: 一个集合中存储了了若干个Student对象, 要求查询出以下结果:
1. 所有及格的学生信息
2. 所有及格的学生姓名
3. 所有学生的平均成绩
4. 班级的前3名(按照成绩)
5. 班级的3-10名(按照成绩)
6. 所有不不及格的学生平均成绩
7. 将及格的学生, 按照成绩降序输出所有信息
8. 班级学生的总分
public class Student {
private String name;
private int score;
public String getName() {
return name;
}
public int getScore() {
return score;
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public String toString() {
return String.format("姓名: %s, 成绩: %d", name, score);
}
}
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.stream.Collectors;
public class Program {
public static void main(String[] args) {
// 0. 实例化集合,存储学生对象
List<Student> list = new ArrayList<>();
Collections.addAll(list,
new Student("xiaoming", 89),
new Student("xiaobai", 98),
new Student("xiaohei", 78),
new Student("xiaolv", 86),
new Student("xiaowang", 59),
new Student("xiaoxiao", 100)
);
// 1. 所有及格的学生信息
System.out.println("-----------1. 所有及格的学生信息-----------");
list.stream()
.filter(s -> s.getScore() >=60)
.forEach(System.out::println);
// 2. 所有及格的学生姓名
System.out.println("-----------2. 所有及格的学生姓名-----------");
list.stream()
.filter(s -> s.getScore() >= 60)
.map(Student::getName)
.forEach(System.out::println);
// 3. 所有学生的平均成绩
System.out.println("-----------3. 所有学生的平均成绩-----------");
double average = list.stream()
.mapToInt(Student::getScore)
.average()
.getAsDouble();
System.out.println(average);
// 4. 班级的前3名(按照成绩)
System.out.println("-----------4. 班级的前3名(按照成绩)-----------");
List<Student> result1 = list.stream()
.sorted((s1, s2) -> s2.getScore() - s1.getScore())
.limit(3)
.collect(Collectors.toList());
result1.forEach(System.out::println);
// 5. 班级的3-10名(按照成绩)
System.out.println("-----------5. 班级的3-10名(按照成绩)-----------");
List<Student> result2 = list.stream()
.sorted((s1, s2) -> s2.getScore() - s1.getScore())
.limit(10)
.skip(2)
.collect(Collectors.toList());
result2.forEach(System.out::println);
// 6. 所有不不及格的学生平均成绩
System.out.println("-----------6. 所有不不及格的学生平均成绩-----------");
double average1 = list.stream()
.filter(s -> s.getScore() < 60)
.mapToInt(Student::getScore)
.average()
.getAsDouble();
System.out.println(average1);
// 7. 将及格的学生, 按照成绩降序输出所有信息
System.out.println("-----------7. 将及格的学生, 按照成绩降序输出所有信息-----------");
list.stream()
.filter(s -> s.getScore() >= 60)
.sorted((s1, s2) -> s2.getScore() - s1.getScore())
.forEach(System.out::println);
// 8. 班级学生的总分
System.out.println("-----------8. 班级学生的总分-----------");
long sum = list.stream()
.mapToInt(Student::getScore)
.sum();
System.out.println(sum);
}
}