JavaAPI(lambda表达式、流式编程)
Lambda表达式
- 本质上就是匿名内部类的简写方式(匿名内部类见:JAVA面向对象3(抽象类、接口、内部类、枚举)-CSDN博客)
- 该表达式只能作用于函数式接口,函数式接口就是只有一个抽象方法的接口。 可以使用注解@FunctionalInterface来验证
public class LambdaDemo01 {
public static void main(String[] args) {
// InterA x = (int a, int b) -> {
// return a + b;
// };
//继续简化:
// InterA x = (a,b) -> {
// return a + b;
// };
//继续简化
InterA x = (a,b) -> a + b;//lambda表达式
int result = x.sum(10, 20);
System.out.println(result);
}
}
@FunctionalInterface //函数式接口
interface InterA{
int sum(int a, int b);
}
Lambda基础语法
- (parameters) -> expression //(参数)->表达式
- (parameters) -> { statements;} //(参数)->{代码块}
注意:
箭头(->)将参数与Lambda主体分隔开来
参数部分:
参数可以是任意合法的Java参数列表,可以为空或包含一个或多个参数。
参数列表的类型名可以省略。 (全部省略或者全部不省略)
如果参数列表中,参数的数量有且只有一个,则小括号可以省略。
Lambda主体:
Lambda主体可以是一个表达式,表达式外的大括号,可加可不加。 没有大括号时,return关键字必须省略。
也可以是一个代码块。将按照常规的Java语法执行,并且您可能需要使用return语句来返回值。
Lambda表达式的应用:
第一种: 没有参数没有返回值的lambda表达式
interface NoParameterNoReturn {
void print();
}主函数:
NoParameterNoReturn i1 = () -> {
int a = 10;
int b = 20;
System.out.println("没有参数没有返回值的lambda表达式" + (a + b));
};
i1.print();
第二种: 没有参数但是有返回值
interface NoParameterReturn {
int print();
}主函数:
NoParameterReturn i2 = () -> {int a = 100; int b = 200; return a + b;};
NoParameterReturn i2 = () -> 100 + 200;
int r1 = i2.print();
System.out.println("r1:"+r1);
第三种:一个参数没有返回值
interface oneParameterNoReturn {
void print(int a);
}主函数:
oneParameterNoReturn i3 = a -> { int b = a * 10; int c = b +2;
System.out.println(c);};
i3.print(200);
第四种:一个参数有返回值
interface oneParameterReturn {
int print(int a);
}主函数:
oneParameterReturn i4 = x -> x*10+2;
int r2 = i4.print(200);
System.out.println(r2);
第五种:多个参数没有返回值
interface multiParameterNoReturn {
void print(int a, int b);
}主函数:
multiParameterNoReturn i5 = (x,y) -> System.out.println("两个参数的乘积:"+x*y);
i5.print(100,200);
第六种: 多个参数有返回值
interface multiParameterReturn {
int print(int a, int b);
}主函数:
multiParameterReturn i6 = (m,n) -> m*n;
System.out.println("3和5的乘积:"+i6.print(3,5));
Lambda在集合中的应用
- forEach
- removerlf
list迭代:
//创建一个集合,存储一堆姓名
List<String> names = new ArrayList<>();
names.add("tom");
names.add("jack");
names.add("james");
names.add("jane");
names.add("lucy");
names.add("lily");
names.forEach(m-> System.out.println(m));
可以写成:
names.forEach(System.out::println);
解释::: 双冒号表示引用,前面的部分可能是类名或者是实例名,后面就是表示要引用他们的方法,注意引用的方法不能带括号
这样写,相当于将类或者对象的方法逻辑,传入给了函数式接口的抽象方法,作为抽象方法的逻辑。
Set迭代 :
Set<String> emps = new HashSet<>();
emps.add("tom");
emps.add("jack");
emps.add("james");
emps.add("jane");
emps.add("lucy");
emps.add("lily");
// emps.forEach(e->System.out.println(e));
emps.forEach(System.out::println);
Map迭代 :
Map<String,String> maps = new HashMap<>();
maps.put("1","jack");
maps.put("2","jane");
maps.put("3","lucy");
maps.put("4","lily");
maps.forEach((k,v)-> System.out.println(Integer.parseInt(k)*10+","+v));//将key字符串类型转换成整数类型,并将key值乘10,加“,”拼接valuse值removeIf按照条件移除元素:
emps.removeIf(m->m.equals("xiaowang"));//如果有xiaowang就移除
System.out.println(emps);
变量的捕获
匿名内部类的变量捕获
在Java中,匿名内部类可以捕获外部变量,即在匿名内部类中引用并访问外部作用域的变量。这种行为称为变量捕获(Variable Capturing)。
-
实例变量(Instance Variables):如果匿名内部类位于一个实例方法中,它可以捕获并访问该实例的实例变量。
-
静态变量(Static Variables):匿名内部类可以捕获并访问包含它的类的静态变量。
-
方法参数(Method Parameters):匿名内部类可以捕获并访问包含它的方法的参数。
-
本地变量(Local Variables):匿名内部类可以捕获并访问声明为final的本地变量。从Java 8开始,final关键字可以省略,但该变量实际上必须是最终的(即不可修改)。
package com;
public class OuterClass {
private int instanceVariable = 10;
private static int staticVariable = 20;public void method(String name) {
final int localVar = 30; // Java 8+ 以后,final可以省略PrintInter pi = new PrintInter() {
@Override
public void print() {
System.out.println("Instance variable: " + instanceVariable);
System.out.println("Static variable: " + staticVariable);
System.out.println("method variable: " + name);
System.out.println("Local variable: " + localVar);
}
};pi.print();
}public static void main(String[] args) {
OuterClass obj = new OuterClass();
obj.method("老黑");
}
}interface PrintInter{
void print();
}
Lambda表达式的变量捕获
在Lambda表达式中,同样可以捕获外部作用域的变量。Lambda表达式可以捕获以下类型的变量:
-
实例变量(Instance Variables):Lambda表达式可以捕获并访问包含它的实例的实例变量。
-
静态变量(Static Variables):Lambda表达式可以捕获并访问包含它的类的静态变量。
-
方法参数(Method Parameters):Lambda表达式可以捕获并访问包含它的方法的参数。
-
本地变量(Local Variables):Lambda表达式可以捕获并访问声明为final的本地变量。从Java 8开始,final关键字可以省略,但该变量实际上必须是最终的(即不可修改)
public class LambdaVariableCapture {
private int instanceVariable = 10;
private static int staticVariable = 20;public void method(String name) {
int localVar = 30;// Lambda表达式捕获外部变量
PrintInter pi = () -> {
@Override
public void print() {
System.out.println("Instance variable: " + instanceVariable);
System.out.println("Static variable: " + staticVariable);
System.out.println("method variable: " + name);
System.out.println("Local variable: " + localVar);
}
};pi.print();
}
}
interface PrintInter{
void print();
}
集合的流式编程
如果想要进行集合的流式编程,必须使用集合实例调用其stream方法
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream();
1.获取每个元素的长度
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<Integer> nameLengths = names.stream() .map(name -> name.length())
.collect(Collectors.toList());System.out.println(nameLengths); //[5, 3, 7]
2.筛选和过滤集合元素
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> filteredNames = names.stream().filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream().filter(number -> number % 2 == 0)
.collect(Collectors.toList());
Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);Map<String, Integer> filteredMap = map.entrySet().stream().filter(entry -> entry.getValue() > 30).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
3.集合排序
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 6, 3, 9, 4, 7, 10);
List<Integer> sortedNumbers = numbers.stream().sorted().collect(Collectors.toList());
4.重写映射Map
Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);Map<String, String> mappedMap = map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> "Age: " + entry.getValue()));
5.对Map的键或值进行归约操作
Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 35);int sumOfValues = map.values().stream().reduce(0, (a, b) -> a + b);
//reduce方法是对流中的元素进行聚合处理,接收两个参数。
//0是初始的累加值 (a, b) -> a + b表示将流中的两个值合并在一起。在这里,a和b 分别代表当前累加值和流中的下一个值,a+b意味着将它们相加。
//最后的值是25+30+35=90
String concatenatedKeys = map.keySet().stream().reduce("", (a, b) -> a + b);//“”初始值 表示一个空字符串,将用于连接所有的键
//a是累计的字符串 b是流中的下一个键。
//结果是"AliceBobCharlie"
Lambda表达式的优缺点
优点:
1.简洁性:Lambda表达式提供了一种更简洁、更紧凑的语法,可以减少冗余的代码和样板代码,使代码更易于理解和维护。
2.代码可读性:Lambda表达式使得代码更加自解释和易读,可以直接将逻辑集中在一起,提高代码的可读性和可维护性。
3.便于并行处理:Lambda表达式与Java 8引入的Stream API结合使用,可以方便地进行集合的并行处理,充分发挥多核处理器的优势,提高代码的执行效率。
4.避免匿名内部类的繁琐语法:相比于使用匿名内部类,Lambda表达式的语法更为简洁,减少了冗余的代码,提高了编码效率。
缺点:
1.只能用于函数式接口:Lambda表达式只能用于函数式接口(只有一个抽象方法的接口),这限制了它的使用范围。如果需要使用非函数式接口,仍然需要使用传统的方式,如匿名内部类。
2.可读性的折衷:尽管Lambda表达式可以提高代码的可读性,但在某些复杂的情况下,Lambda表达式可能变得难以理解和阅读,特别是当表达式变得过于复杂时。
3.变量捕获的限制:Lambda表达式对捕获的变量有一些限制。它们只能引用final或实际上的最终变量,这可能对某些情况下的代码编写和调试带来一些困扰。
4.学习曲线:对于习惯于传统Java编程风格的开发者来说,Lambda表达式是一项新的概念,需要一定的学习和适应过程。
集合的流式编程
简介
Stream编程的简介
Stream是JDK1.8之后出现的新特性,也是JDK1.8新特性中最值得学习的两种新特性之一。(另外一个是 lambda表达式)。
Stream是对集合操作的增强,流不是集合的元素,不是一种数据结构,不负责数据的存储的,并且和IO流没有任何关系。
流更像是 一个迭代器,可以单向的遍历一个集合中的每一个元素,并且不可循环。
为什么要使用集合的流式编程
有些时候,对集合中的元素进行操作的时候,需要使用到其他操作的结果。
在这个过程中,集合的流式编程可以大幅度的简化代码的数量。
将数据源中的数据,读取到一个流中,可以对这个流中的数据进行操作(删除、过滤、映射...)。每次的操作结果也是一个流对象,可以对这个流再进行其他的操作。
使用流式编程的步骤
-
获取数据源,将数据源中的数据读取到流中。
-
对流中的数据进行各种各样的处理。
-
对流中的数据进行整合处理。
过程2中,有若干方法,可以对流中的数据进行各种各样的操作,并且返回流对象本身,这样的操作,被称为中间操作。
过程3中,有若干方法,可以对流中的数据进行各种处理并关闭流,这样的操作,被称为最终操作。
在中间操作和最终操作中,基本上所有的方法参数都是函数式接口,可以使用lambda表达式来实现。 使用集合的流式编程,来简化代码量,但是需要对 lambda 表达式做到熟练掌握。
数据源的获取
数据源,既是流中的数据的来源。
是集合的流式编程的第一步,将数据源中的数据读取到流中,进行处理。
注意:将数据读取到流中进行处理的时候,与数据源中的数据没有关系。也就是说,中间操作对流中的数据进行处理、过滤、映射、排序... ,此时是不会影响数据源中的数据的。
// 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()内部集成了多个线程对流中的数据进行 操作,效率更高。
第一步:获取数据源,其实就是通过要操作的集合来获取流对象
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
nums.add(6);
nums.add(7);
nums.add(8);
// parallelStream(): 是并行运算,即多线程
//Stream<Integer> integerStream = nums.parallelStream();
// 串行运算,单线程
Stream<Integer> stream = nums.stream();
最终操作
将流中的数据整合到一起,可以存入一个集合,也可以直接对流中的数据进行遍历、数据统计... ,通过最终操作,需要掌握如何从流中提取出来我们想要的信息。
注意事项:最终操作,之所以叫最终操作,是因为,在最终操作执行结束后,会关闭这个流,流中的所有数据都会销毁。如果使用一个已经关闭了的流,会出现异常。
collect
将流中的数据收集到一起,对这些数据进行一些处理。
最常见的处理,就是将流中的数据存入一个集合。
collect方法的参数,是一个Collector接口,而且这个接口并不是一个函数式接口。实现这个接口, 可以自定义收集的规则。但是,绝大部分情况下,不需要自定义,直接使用Collectors工具类提供的方法即可
// 1、转成 List
List<Integer> result1 = list.stream().collect(Collectors.toList());
System.out.println(result1);// 2、转成 Set
Set<Integer> result2 = list.stream().collect(Collectors.toSet());
System.out.println(result2);// 3、转成 Map,提供两个函数式接口的实现,分别实现键的生成规则和值的生成规则
Map<Integer, Integer> result3 = list.stream().collect(Collectors.toMap(ele ->ele / 10, ele -> ele));
System.out.println(result3);
reduce
将流中的数据按照一定的规则聚合起来。
两个形参, m,n第一次分别接受第一个和第二个元素, 然后m作为结果并返回,
我们的自定义逻辑是m+n, 因此相当于 m = m +n,
之后的每一个元素都分别赋值给n,m再次当成参数传入。
// 将流的元素,逐一带入到这个方法中,进行运算
// 最终的运算结果,得到的其实是一个 Optional 类型,需要使用get() 获取到里面的数据
int result4 = list.stream().reduce((e1, e2) -> e1 + e2).get();
System.out.println(result4);
count
统计流中的元素数量。
long result5 = list.stream().count();
System.out.println(result5);
forEach
迭代、遍历流中的数据
list.stream().forEach(System.out::println);
max & min
获取流中的最大的元素、最小的元素。
// 获取最大值
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);
Matching
allMatch: 只有当流中所有的元素,都匹配指定的规则,才会返回 true
anyMatch: 只要流中有任意的数据,满足指定的规则,都会返回 true
noneMatch: 只有当流中的所有的元素,都不满足指定的规则,才会返回true
// 判断流中是否所有的元素都大于 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返回的结果可能不一样。
Integer result11 = list.parallelStream().findFirst().get();
System.out.println(result11);Integer result12 = list.parallelStream().findAny().get();
System.out.println(result12);
最终操作的注意事项
最终操作,会关闭流。如果一个流被关闭了,再去使用这个流,就出出现异常。
// 最终操作错误示范
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)
中间操作
这里先介绍个JUnit测试类:
@Test:该注解有mian方法的功能,能启动JVM虚拟机来运行代码片段
该注解只能放在非静态方法上面。JVM运行的是该注解下面的方法
需要导入两个jar包:
1.和src平级的关系,创建一个文件夹lib
2.放入这两个jar包
3. 全选两个jar包文件,右键点击。选择 add as library
@Before:也是放在某一个非静态方法上,该注解所在的方法会先于@Test注解所在的方法之前运行。
@Before
public void pre(){
List<Integer> nums = new ArrayList<>();
nums.add(1);
nums.add(2);
nums.add(3);
nums.add(4);
nums.add(5);
nums.add(3);
nums.add(4);
nums.add(6);
stream = nums.stream();
}
filter
用来过滤符合某些条件的元素。
@Test
public void testFilter(){
List<Integer> result = stream.filter(e -> e % 2 == 0).collect(Collectors.toList());
System.out.println(result);
}
distinct
去掉流中的重复操作
@Test
public void testDistinct(){
List<Integer> collect = stream.distinct().collect(Collectors.toList());
System.out.println(collect);
}
sorted
排序
sorted: 排序
sorted(): 默认升序排序
sorted(...) 降序要比较器@Test
public void testSort(){
//默认升序排序
// stream.sorted().forEach(System.out::println);
stream.sorted((a,b)->b-a).forEach(System.out::println);
}
limit
获取前n个元素
@Test
public void testLimit(){
stream.limit(2).forEach(System.out::println);
}
skip
跳过n个元素
@Test
public void testSkip(){
stream.skip(2).forEach(System.out::println);
}
map
映射 想要将每一个元素映射成另外一个样子。
@Test
public void testMap(){
List<String> collect = stream.map(e -> e + "0000").collect(Collectors.toList());
System.out.println(collect);
}
flatMap
压平并映射,即可以将二维的数组或者集合转成一维的数组或者集合。
@Test
public void testFlatMap(){
List<List<Integer>> nums = new ArrayList<>();
nums.add(Arrays.asList(1,2,3,4));
nums.add(Arrays.asList(5,6,7,8));
nums.add(Arrays.asList(9,10,11,12));
nums.add(Arrays.asList(13,14,15,16));List<Integer> collect = nums.stream().flatMap(e -> e.stream()).collect(Collectors.toList());
System.out.println(collect);}
mapToInt
针对每个元素 是否有转成int的需求
@Test
public void testMapToInt(){
List<String> names = new ArrayList<>();
names.add("wangcongming");
names.add("zhangsan");
names.add("lisi");
names.add("wangwang");
names.stream().mapToInt(e->e.length()).forEach(System.out::println);
}
Collectors工具类
Collectors是一个工具类,里面封装了很多方法,可以很方便的获取到一个 Collector 接口的实现类对 象,从而可以使用 collect() 方法,对流中的数据,进行各种各样的处理、整合。
Collectors.toList() : 将流中的数据,聚合到一个 List 集合中
Collectors.toSet() : 将流中的数据,聚合到一个 Set 集合中
Collectors.toMap() : 将流中的数据,聚合到一个 Map 集合中
maxBy() : 按照指定的规则,找到流中最大的元素,等同于 max
minBy() : 按照指定的规则,找到流中最小的元素,等同于 minjoining() : 将流中的数据拼接成一个字符串,注意:只能操作流中是String的数据
summingInt() : 将流中的数据,映射成 int 类型的数据,并求和
averagingInt() : 将流中的数据,映射成 int 类型的数据,并求平均值
summarizingInt() : 将流中的数据,映射成 int 类型的数据,并获取描述信息
// maxBy: 按照指定的规则,找到流中最大的元素,等同于 max
Student max = list.stream().collect(Collectors.maxBy((s1, s2) -> s1.getScore() -s2.getScore())).get();
System.out.println(max);// minBy: 按照指定的规则,找到流中最小的元素,等同于 min
Student min = list.stream().collect(Collectors.minBy((s1, s2) -> s1.getScore() -s2.getScore())).get();
System.out.println(min);
// 将流中的数据,拼接起来
String s1 = list.stream().map(Student::getName).collect(Collectors.joining());
System.out.println(s1);// 将流中的数据,拼接起来,以指定的分隔符进行分隔
String s2 = list.stream() .map(Student::getName) .collect(Collectors.joining(", "));
System.out.println(s2);// 将流中的数据,拼接起来,以指定的分隔符进行分隔,并添加前缀和尾缀
String s3 = list.stream().map(Student::getName).collect(Collectors.joining(", ", "{", "}"));
System.out.println(s3);
// 将流中的数据,映射成 int 类型的数据,并求和
int sum = list.stream().collect(Collectors.summingInt(Student::getScore));
System.out.println(sum);
// 将流中的数据,映射成 int 类型的数据,并求平均值
double average = list.stream() .collect(Collectors.averagingInt(Student::getScore));
System.out.println(average);
// 将流中的数据,映射成 int 类型的数据,并获取描述信息
IntSummaryStatistics summaryStatistics = list.stream() .collect(Collectors.summarizingInt(Student::getScore));System.out.println(summaryStatistics);
System.out.println(summaryStatistics.getCount());
System.out.println(summaryStatistics.getSum());
System.out.println(summaryStatistics.getMax());
System.out.println(summaryStatistics.getMin());
System.out.println(summaryStatistics.getAverage());
综合大栗子
需求 : 一个集合中存储了了若干个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)
);
// 所有及格的学生信息
list.stream().filter(s->s.getScore()>=60).forEach(System.out::println);
// 所有及格的学生姓名
list.stream().filter(s->s.getScore()>=60)
.map(Student::getName).forEach(System.out::println);
// 所有学生的平均成绩
double average=list.stream().mapToInt(Student::getScore().average().getAsDouble);
System.out.println(average);
// 班级的前3名(按照成绩)
List<Student> result1=list.stream().sorted((s1,s2)->s2.getScore()-s1.getScore()).limit(3).collect(Collectors.toList());
result1.forEach(System.out::println);
// 班级的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);// 所有不不及格的学生平均成绩
double average1 = list.stream().filter(s -> s.getScore() < 60).mapToInt(Student::getScore)
.average() .getAsDouble();
System.out.println(average1);// 将及格的学生 , 按照成绩降序输出所有信息
list.stream().filter(s -> s.getScore() >= 60).sorted((s1, s2) -> s2.getScore() - s1.getScore()).forEach(System.out::println);
// 班级学生的总分
long sum = list.stream().mapToInt(Student::getScore).sum();
System.out.println(sum);