第十四章lambda表达式与流处理

14.1 Iambda表达式

Iambda表达式简介

lambda表达式可以用非常少的代码实现抽象方法。
lambda表达式不能独立执行,因此必须实现函数式接口,并且会返回一个函数式接口的对象。
lambdab表达式的语法非常特殊

语法格式:
()-> 结果表达式 
参数-> 结果表达式
(参数1,参数2...,参数n)-> 结果表达式

  • 第1行实现无参方法,单独写一对圆括号表示方法无参数,操作符右侧的结果表达式表示方法的返回值。
  • 第2行实现只有一个参数的方法,参数可以写在圆括号里,或者不写圆括号。
  • 第3行实现多参数的方法,所有参数按顺序写在圆括号里,且圆括号不可以省略。

lambda表达式也可以实现复杂方法,将操作符右侧的结果表达式换成代码块即可

语法格式如下:
()->{代码块)
参数->(代码块}
(参数1,参数2,..参数n)->{代码块)

  • 第1行实现无参方法,方法体是操作符右侧代码块。
  • 第2行实现只有一个参数的方法,方法体是操作符右侧代码块。
  • 第3行实现多参数的方法,方法体是操作符右侧代码块。

lambda表达式的功能归纳总结,语法理解为:

()                       ->               {代码块}
这个方法            按照              这样的代码来实现

Iambda表达式实现函数式接口

1.函数式接口

开发者可以常见自定义的函数式接口

例如:
interface MyInterface{
        void method();
}

如果接口中包含一个以上的抽象方法,则不符合函数式接口的规范,这样的接口不能用Iambda表达式创建匿名对象


2.Iambda表达式实现无参数抽象方法

例题14.1

interface SayHiInterface{	//例题14.1String say();//抽象方法接口}public class NoParamterDemo {public static void main(String[] args) {//利用匿名内部类补全方法体//lambda表达式实现发招呼接口,返回抽象方法结果SayHiInterface pi=()->"你好啊,这是lanbda表达式";System.out.print(pi.say());}

运行结果如下:

3.lambda表达式实现有参抽象方法

如果抽象方法中只有一个参数,lambda表达式则可以省略圆括号
例题14.2

interface AddInt{int add(int a,int b);
}
public class ParamDemo {public static void main(String[] args) {// TODO Auto-generated method stub
/*无参数      AddInt ai1 = new AddInt() {public int add(int a,int b) {return a+b;}};System.out.println("匿名内部类:" + ai1.add(3,5));//使用Lambda表示式补全方法体AddInt ai2 = (a, b) ->{return a+b;};System.out.println("lambda表达式:" + ai2.add(3,5));
}
}*/AddInt np=(x,y)->x+y;		//表达式int result=np.add(15,26);					//调用接口方法System.out.println("相加结果:"+result);			//输出相加结果}
}

运行结果如下:

lambda表达式中的参数不需要与抽象方法的参数名称相同,但顺序必须相同


4.lambda表达式使用代码块

lambda表达式会自动判断返回值类型是否符合抽象方法的定义
例题14.3

interface CheckGrade{			//例题14.3String check(int grade);    //查询成绩结果
}
public class GradeDemo {public static void main(String[] args) {// TODO Auto-generated method stubCheckGrade g =(n)-> {			//lambda表达式实现代码块if (n>=90&& n<=100) {		//如果成绩在90~100return"成绩为优";			//输出成绩为优}else if (n>=80&& n<=90) {	//如果成绩在80~90return"成绩为良";			//输出成绩为良}else if (n>=60&& n<=80) {	//如果成绩在60~80return"成绩为中";			//输出成绩为中}else if (n>=0&& n<=60) {	//如果成绩在00~60return"成绩为差";			//输出成绩为差}else {						//其他数字不是有效成绩return"成绩无效";			//输出成绩无效}
};System.out.println(g.check(89));}
}

运行结果如下:

Iambda表达式调用外部变量 

这些外部的变量有些可以被更改,有些则不能。例如,lambda表达式无法更改局部变量的值,但是却可以更改外部类的成员变量(也可以叫做类属性)的值他。


1.lambda表达式无法更改局部变量

局部变量在lambda表达式中默认被定义为final(静态)的,也就是说,lambda表达式只能调用局部变量,却不能改变其值。
例题14.4

interface VariableInterface1{		//例题14.4void method();
}
public class VariableDemo1 {public static void main(String[] args) {// TODO Auto-generated method stubint value=100;VariableInterface1 v=()->{int num=value-90;value=12;};}}

运行结果如下:

2.lambda表达式可以更改类成员变量

类成员变量是在lambda表达式中不是被final修饰的,所以lambda表达式可以改变其值
例题14.5

interface VariableInterface2{					//测试接口void method();								//测试方法
}public class VariableDemo2 {					//测试类int value = 100;							//创建类成员变量public void action() {						//创建类成员方法VariableInterface2 v=()->{				//实现测试接口value =-12;							//更改成员变量,没提示任何错误};System.out.println("运行接口方法前value="+value);  //运行接口方法前先输出成员变量值v.method();										 //运行接口方法System.out.println("运行接口方法后value="+value); //运行接口方法后再输出成员变量值}public static void main(String[] args) {VariableDemo2 demo = new VariableDemo2();	//创建测试类对象demo.action();								//执行测试类方法}}

运行结果如下:

  • lambda 表达式可以调用并修改类成员变量的值
  • lambda表达式只是描述了抽象方法是如何实现的,在抽象方法没有被调用前,lambda表达式中的代码并没有被执行,所以运行抽象方法之前类成员变量的值不会发生变化。
  • 只要抽象方法被调用,就会执行lambda 表达式中的代码,类成员变量的值就会被修改。


Iambda表达式与异常处理

lambda 表达式中并有抛出异常的语法,这是因为lambda表达式会默认抛出抽象方法原有的异常,当此方法被调用时则要进行异常处理。
例题14.6

import java.util.Scanner;											
interface AntiaddictInterface{											//防沉迷接口boolean check(int age)throws UnderAgeException;						//抽象检查方法,抛出用户未成年异常
}class UnderAgeException extends Exception{								//自定义未成年异常public UnderAgeException(String message) {							//有参构造方法super(message);													//调用原有父类构造方法}
}public class ThrowExceptionDemo {										//测试类public static void main(String[] args) {							//主方法//lambda表达式创建AntiaddictInterface对象,默认抛出原有异常AntiaddictInterface ai =(a)->{if(a<18) {													//如果年龄小于18岁throw new UnderAgeException("未满18周岁,开启防沉迷模式!");		//抛出异常}else {														//否则return true;											//验证通过}};Scanner sc = new Scanner(System.in);							//创建控制台扫描器System.out.print("请输入年龄:");									//控制台提示int age = sc.nextInt();											//获取用户输入的年龄try {															//因为接口方法抛出异常,所以此处必须捕捉异常if(ai.check(age)) {											//验证年龄System.out.println("欢迎进入XX世界");}}catch(UnderAgeException e) {									//控制台打印异常警告System.out.println(e);}sc.close();														//关闭扫描器}
}

运行结果如下:

14.2方法的引用

引用静态方法

语法:
类名::静态方法名 

新的操作符“::”,中间无空格,左边表示方法所属的类名,右边是方法名,语法中方法名是没有括号的。 
例题14.7


interface StaticMethodInterface{		//测试接口  例题14.7int method(int a, int b);			//抽象方法
}
public class StaticMethodDemo {static int add(int x,int y) {		//静态方法,返回两个参数相加的结果return x +y;					//返回相加结果}public static void main(String[] args) {// TODO Auto-generated method stubStaticMethodInterface sm= StaticMethodDemo::add;	//引用StaticMethodDemo类的静态方法int result = sm.method(15,16);						//直接调用接口方法获取结果System.out.println("接口方法结果:"+result);				//输出结果}}

运行结果如下:

引用成员方法

引用成员方法的语法:
对象名::成员方法名

操作符左侧必须是一个对象名,而不是类名。 
例题14.8
 

import java.text.SimpleDateFormat;			//例题14.8
import java.util.Date;
interface InstanceMethodInterface{			//测试创建接口String method(Date date);				//带参数的抽象方法
}
public class InstanceMethodDemo {public String format(Date date) {		//格式化方法//创建日期格式化对象,并指定日期格式SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd");return sdf.format(date);			//返回格式化结果}public static void main(String[] args) {// TODO Auto-generated method stubInstanceMethodDemo demo = new InstanceMethodDemo();			//创建类对象InstanceMethodInterface im = demo::format;					//引用类对象的方法Date date = new Date();										//创建日期对象System.out.println("默认格式:"+date);							//输出日期对象默认格式System.out.println("接口输出的格式:"+im.method(date));			//输出经过接口方法处理过的格式}}

运行结果如下:

引用带泛型的方法 (14.9/12/13不用写博客)

引用构造方法

Iambda表达式有3种引用构造方法的语法,分别是引用无参数构造方法、引用有参构造方法和引用数构造方法


1.引用无参构造方法

语法:
类名::new

注意:new关键字之后没有圆括号,也没有参数的定义 
例题14.10

interface ConstructorsInterface1{								//构造方法接口ConstructorsDemo1 action();									//调用无参方法
}
public class ConstructorsDemo1 {								//测试类public ConstructorsDemo1() {								//无参构造方法System.out.print("无参构造方法");}public ConstructorsDemo1(int a) {							//有参构造方法System.out.print("有参构造方法"+ a);}public static void main(String[] args) {				ConstructorsInterface1 ci = ConstructorsDemo1::new;		//引用ConstructorsDemo1类的构造方法ci.action();											//通过无参方法创建对象}}

运行结果如下:

2.引用有参构造方法

引用有参构造方法的语法与引用无参构造方法一样。区别就是函数式接口的抽象方法是有参数的
例题14.11


interface ConstructorsInterface2{								//构造方法接口ConstructorsDemo2 action(int i);							//调用有参方法
}
public class ConstructorsDemo2 {								//测试类public ConstructorsDemo2(){									//无参构造方法	System.out.print("调用无参构造方法");}public ConstructorsDemo2(int i) {							//有参构造方法System.out.println("有参构造方法,参数为:"+i);}public static void main(String[] args) {					ConstructorsInterface2 a = ConstructorsDemo2::new;		//引用ConstructorsDemo1类的构造方法ConstructorsDemo2 b = a.action(123);					//通过无参方法创建对象}}

运行结果如下:

3.引用数组构造方法

语法;
类名[]::new
 

14.2.5Fuction接口 

这个接口有以下两个泛型:
T:被操作的类型,可以理解为方法参数类型。
R:操作结果类型,可以理解为方法的返回类型。
Function 接口是函数式接口,所以只有一个抽象方法,但是Function 接口还提供
方法以方便开发者对函数逻辑进行更深层的处理。
Function 接口方法如表14.1所示

14.3流处理

流处理有点类似数据库的 SQL 语句,可以执行非常复杂的过滤、映射、查找和收集功能,并且代码量很少。唯一的缺点是代码可读性不高。
对员工数据进行流处理
员工集合的详细数据如下

例题14.14

import java.util.ArrayList;
import java.util.List;public class Employee {private String name;	//姓名private int age;		//年龄private double salary;	//工资private String sex;		//性别private String dept;	//部门public Employee(String name, int age, double salary, String sex, String dept) {super();this.name = name;this.age = age;this.salary = salary;this.sex = sex;this.dept = dept;}@Overridepublic String toString() {return "Employee [name=" + name + ", age=" + age + ", salary=" + salary + ", sex=" + sex + ", dept=" + dept+ "]";}public String getName() {return name;}public int getAge() {return age;}public double getSalary() {return salary;}public String getSex() {return sex;}public String getDept() {return dept;}static List<Employee> getEmpList(){List<Employee> list=new ArrayList<Employee>();list.add(new Employee("老张",40,9000,"男","运营部"));list.add(new Employee("小刘",24,5000,"女","开发部"));list.add(new Employee("大刚",32,7500,"男","销售部"));list.add(new Employee("翠花",28,5500,"女","销售部"));list.add(new Employee("小马",21,3000,"男","开发部"));list.add(new Employee("老王",35,6000,"女","人事部"));list.add(new Employee("小王",21,3000,"女","人事部"));return list;}
}

方法如下:Source——Generate ConStructor using Fields

Source——Generate toStrinig()

Source——Generate Getterd and Setters

点击右边的Select Getters

14.3.1 Stream接口简介

流处理的接口都定义在java.uil.stream 包下。BaseStream接口是最基础的接口,但最常用的是BaseStream 接口的一个子接口——Stream 接口,基本上绝大多数的流处理都是在Stream 接口上实现的。Stream 接口是泛型接口,所以流中操作的元素可以是任何类的对象。
Stream 接口的常用方法如表14.3所示。

Collection 接口新增两个可以获取流对象的方法。第一个方法最常用,可以获取集合的顺序流,方法如下:


Stream<E> stream();

第二个方法可以获取集合的并行流,方法如下:

Stream<E> parallelstream();

因为所有集合类都是Collection 接口的子类,如ArrayList类、HashSet类等,所以这些类都可以进行流处理。例如:

List<integer> list = new ArrayList<Integer>();  //创建集合
Streamcinteger> s = list.stream();//获取集合流对象

14.3.2 Optional类


 Optional 类是用final 修饰的,所以不能有子类。Optional类是带有泛型的类,所以该类可以保存
任何对象的值。
从Optional类的声明代码中就可以看出这些特性,JDK中的部分代码如下:

public final class Optional<T>{
private final T value;
} //省略其他代码

Optional 类中有一个叫作value的成员属性,这个属性就是用来保存具体值的。value 是用泛型了
修饰的,并且还用了final 修饰,这表示一个 Optional 对象只能保存一个值。
Optional类提供了很多封装、校验和获取值的方法,这些方法如下:
例题14.15

import java.util.Optional;public class OptionalDemo {public static void main(String[] args) {Optional<String>strValue = Optional.of("hello");			//创建有值对象boolean haveValueFlag = strValue.isPresent();				//判断对象中的值是不是空的System.out.println("strValue对象是否有值:"+haveValueFlag);if(haveValueFlag) {											//如果不是空的String str = strValue.get();							//获取对象中的值System.out.println("strValue对象的值是:"+str);}Optional<String> noValue = Optional.empty();				//创建空值对象boolean noValueFlag = noValue.isPresent();					//判断对象中的值是不是空的System.out.println("noValue对象是否有值:"+noValueFlag);if(noValueFlag) {											//如果不是空的String str = noValue.get();								//获取对象中的值System.out.println("noValue对象的值是:"+str);	}else {														//如果是空的String str = noValue.orElse("使用默认值");					//使用默认值System.out.println("noValue对象是:"+str);}}}

运行结果如下:

14.3.3 Collectors类 

此类提供了很多实用的数据加工方法,如下:

14.3.4 数据过滤 

数据过滤就是在杂乱的数据中筛选出需要的数据,类似 SQL 语句中的WHERE 关键字,给出一定
的条件,将符合条件的数据过滤并展示出来。
1. filter()方法

filterO方法是Stream 接口提供的过滤方法。该方法可以将lambda表达式作为参数,然后按照lambda表达式的逻辑过滤流中的元素。过滤出想要的流元素后,还需使用Stream 提供的collectO方法按照指定方法重新封装。
例题14.16

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;public class FilterOddDemo {static void printeach(String message,List list) {			//输出集合元素System.out.print(message);								//输出文字信息list.stream().forEach(n->{								//使用forEach方法遍历集合并打印元素System.out.print(n+"");});System.out.println();									//换行}public static void main(String[] args) {List<Integer> list = new ArrayList<>();						//创建空数组for(int i = 1; i<=10;i++) {									//从1循环到10list.add(i);											//给集合赋值}printeach("集合原有元素:",list);								//输出集合元素Stream<Integer> stream = list.stream();						//获取集合流对象//将集合中的所有奇数过滤出来,把过滤结果重新赋值给流对象stream = stream.filter(n->n%2==1);//将流对象重新封装成一个List集合List<Integer> result = stream.collect(Collectors.toList());printeach("过滤之后的集合元素:",result);							//输出集合元素
}
}

运行结果如下:

“获取流”“过滤流”“封装流”这三部分可以写在同一行代码中 
List<Integer> result = list.stream.().filter(n->%2==1).collect(Collectors.toList());

例题14.17

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;public class FilerDemo{	   //例题14.17public static void main(String[] args) {List<Employee> list=Employee.getEmpList();//获取公共类的测试数据Stream<Employee> stream = list.stream();//获取集合流对象//筛选年龄>30岁的员工stream=stream.filter(sx-> sx.getAge()>30);//将年龄大于30岁的员工过滤出来//限制条数stream = stream.limit(2);//将流对象重新封装成一个List集合List<Employee> result = stream.collect(Collectors.toList());//遍历结果集for (Employee sx : result) {//输出员工对象信息System.out.println(sx);}}}

运行结果如下:

2. distinct()方法 

该方法可以去除流中的重复元素,效果与SQL语句中的DISTINCT关键字一样
例题14.18

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;public class DistinctDemo {  //例题14.18static void printeach(String message,List list) {	//输出集合元素System.out.print(message);						//输出文字信息list.stream().forEach(n->{						//使用fourEach方法遍历集合并打印元素System.out.println(n+"");});System.out.println();							//换行}public static void main(String[] args) {// TODO Auto-generated method stubList<Integer>list=new ArrayList<Integer>();	//创建集合list.add(1);									//添加元素list.add(2);list.add(2);list.add(3);list.add(3);printeach("去重前:",list);						//打印集合元素Stream<Integer>stream=list.stream();			//获取集合流对象stream=stream.distinct();						//取出流中的重复元素List<Integer>reslut=stream.collect(Collectors.toList());//将流对象重新封装成一个List集合printeach("去重后:",reslut);}}

运行结果如下:

3. Iimit()方法

Iimit()方法是Stream接口提供的方法,该方法可以获取流中前N个元素
例题14.19
 

import java.util.List;
import java.util.stream.Collectors;
import java.util. stream.Stream;public class LimitDemo {		//例题14.19public static void main(String[] args){List<Employee> list = Employee.getEmpList();//获取公共类的测试数据Stream<Employee> stream = list.stream();//获取集合流对象stream = stream.filter(people ->"女".equals(people.getSex()));//将所有女员工过滤出来stream = stream.limit(2);//取出前两位List<Employee> result = stream.collect(Collectors.toList());//将流对象重新封装成一个List集合for (Employee emp : result) {//遍历结果集System.out.println(emp);//输出员工对象信息}}}

运行结果:

4. skip方法 

skip()方法是Stream接口提供的方法,该方法可以忽略流中的前N个元素
例题14.20
 

import java.util.List;
import java.util.stream.Collectors;
import java.util. stream.Stream;
public class SkipDemo {   //例题14.14public static void main(String[] args) {// TODO Auto-generated method stubList<Employee> list = Employee.getEmpList();//获取公共类的测试数据Stream<Employee> stream = list.stream();//获取集合流对象stream = stream.filter(people ->"男".equals(people.getSex()));//将所有男员工过滤出来stream = stream.skip(2);//取出前两位List<Employee> result = stream.collect(Collectors.toList());//将流对象重新封装成一个List集合for (Employee emp : result) {//遍历结果集System.out.println(emp);//输出员工对象信息}}}

运行结果:

14.3.5数据映射 

数据的映射和过滤概念不同:过滤是在流中找到符合条件的元素,映射是在流中获得具体的数据。Stream 接口提供了mapO方法用来实现数据映射,mapO方法会按照参数中的函数逻辑获取新的流对象,新的流对象中元素类型可能与旧流对象元素类型不相同。
例题14.21

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;public class MapDemo {public static void main(String[] args) {List<Employee> list = Employee.getEmpList();          // 获取公共类的测试数据Stream<Employee> stream = list.stream();              // 获取集合流对象// 将所有开发部的员工过滤出来stream = stream.filter(people -> "开发部".equals(people.getDept()));// 将所有员工的名字映射成一个新的流对象Stream<String> names = stream.map(Employee::getName);// 将流对象重新封装成一个List集合List<String> result = names.collect(Collectors.toList());for (String emp : result) {                              // 遍历结果集System.out.println(emp);                             // 输出所有姓名}}
}

运行结果如下:

例题14.22

import java.util.List;
import java.util.stream.DoubleStream;
import java.util.stream.Stream;
public class MapToInDemo {public static void main(String[] args) {List<Employee> list = Employee.getEmpList();      // 获取公共类的测试数据Stream<Employee> stream = list.stream();          // 获取集合流对象// 将所有开发部的员工过滤出来stream = stream.filter(people -> "销售部".equals(people.getDept()));// 将所有员工的名字映射成一个新的流对象DoubleStream salarys = stream.mapToDouble(Employee::getSalary);// 统计流中元素的数学总和double sum = salarys.sum();System.out.println("销售部一个月的薪资总额:"+sum);}
}

运行结果如下:

14.3.6 数据查找

1.allMatch()方法

该方法会判断流中的元素是否全部符合某一条件,返回结果是boolean值
例题14.23

import java.util.List;
import java.util.stream.Stream;public class AllMatchDemo {public static void main(String[] args) {List<Employee> list = Employee.getEmpList();       // 获取公共类的测试数据Stream<Employee> stream = list.stream();           // 获取集合流对象// 判断所有员工的年龄是否都大于25boolean result = stream.allMatch(people -> people.getAge() > 25);System.out.println("所有员工是否都大于25岁:" + result);  // 输出结果}
}

运行结果如下:

2.anyMatch方法()方法

该方法会判断流中的元素是否有符合某一条件
例题14.24

import java.util.List;
import java.util.stream.Stream;
public class AnyMatchDemo {public static void main(String[] args) {List<Employee> list = Employee.getEmpList();     // 获取公共类的测试数据Stream<Employee> stream = list.stream();         // 获取集合流对象// 判断员工是否有的年龄大于等于40boolean result = stream.anyMatch(people -> people.getAge() >= 40);System.out.println("员工中有年龄在40或以上的吗?:" + result); // 输出结果}
}

运行结果如下:

3.noneMatch()方法

该方法会判断流中的所有元素是否都不符合某一条件
例题14.25

import java.util.List;
import java.util.stream.Stream;
public class NoneMathchDemo {public static void main(String[] args) {List<Employee> list = Employee.getEmpList();        // 获取公共类的测试数据Stream<Employee> stream = list.stream();            // 获取集合流对象// 判断公司中是否不存在薪资小于2000的员工?boolean result = stream.noneMatch(people -> people.getSalary() <2000 );System.out.println("公司中是否不存在薪资小于2000元的员工?:" + result);// 输出结果}
}

运行结果如下:

4.findFirst()方法

这个方法会返回符合条件的第一个元素
例题14.26

import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public class FindFirstDemo {public static void main(String[] args) {// 获取公共类的测试数据List<Employee> list = Employee.getEmpList();Stream<Employee> stream = list.stream();             // 获取集合流对象// 过滤出21岁的员工stream = stream.filter(people -> people.getAge() == 21);Optional<Employee> young = stream.findFirst();      // 获取第一个元素Employee emp = young.get();                           // 获取员工对象System.out.println(emp);                              // 输出结果}
}

运行结果如下:

这个方法的返回值不是boolean值,而是一个Optional对象


14.3.7 数据收集

1.数据统计

不仅可以筛选出特殊元素,还可以对元素的属性进行统计计算
例题14.27

import java.util.Comparator; // 比较器接口
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;public class ReducingDemo {public static void main(String[] args) {List<Employee> list = Employee.getEmpList(); // 获取测试数据long count = list.stream().count(); // 获取总人数// 下行代码也能实现获取总人数效果// count = stream.collect(Collectors.counting());System.out.println("公司总人数为:" + count);// 通过Comparator比较接口,比较员工年龄,再通过Collectors的maxBy()方法取出年龄最大的员工的Optional对象Optional<Employee> ageMax = list.stream().collect(Collectors.maxBy(Comparator.comparing(Employee::getAge)));Employee older = ageMax.get();// 获取员工对象System.out.println("公司年龄最大的员工是:\n    " + older);// 通过Comparator比较接口,比较员工年龄,再通过Collectors的minBy()方法取出年龄最小的员工的Optional对象Optional<Employee> ageMin = list.stream().collect(Collectors.minBy(Comparator.comparing(Employee::getAge)));Employee younger = ageMin.get();// 获取员工对象System.out.println("公司年龄最小的员工是:\n    " + younger);// 统计公司员工薪资总和double salarySum = list.stream().collect(Collectors.summingDouble(Employee::getSalary));System.out.println("公司的薪资总和为:" + salarySum); // 输出结果// 统计公司薪资平均数double salaryAvg = list.stream().collect(Collectors.averagingDouble(Employee::getSalary));// 使用格式化输出,保留2位小数System.out.printf("公司的平均薪资为:%.2f\n", salaryAvg);// 创建统计对象,利用summarizingDouble()方法获取员工薪资各方面的统计数据java.util.DoubleSummaryStatistics s = list.stream().collect(Collectors.summarizingDouble(Employee::getSalary));System.out.print("统计:拿薪资的人数=" + s.getCount() + ", ");System.out.print("薪资总数=" + s.getSum() + ", ");System.out.print("平均薪资=" + s.getAverage() + ", ");System.out.print("最大薪资=" + s.getMax() + ", ");System.out.print("最小薪资=" + s.getMin() + "\n");// 将公司员工姓名拼成一个字符串,用逗号分隔String nameList = list.stream().map(Employee::getName).collect(Collectors.joining(", "));System.out.println("公司员工名单如下:\n    " + nameList);}
}

运行结果如下:

2.数据分组
将流中元素按照指定的条件分开保存
数据分组有一级分组和多级分组
一级分组:将所有数据按照一个条件进行分类
例题14.28
 

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;public class GroupDemo1{public static void main(String[] args) {// TODO Auto-generated method stubList<Employee> list = Employee.getEmpList();Stream<Employee>stream=list.stream();Map<String,List <Employee>> map = stream.collect(Collectors.groupingBy(Employee::getDept));Set<String>depts=map.keySet();for(String dept:depts) {System.out.println(dept+"员工信息如下:");List<Employee>temp=map.get(dept);for(Employee e:temp) {System.out.println(e);}System.out.println();}}}

运行结果如下:

难点:
分组规则是一个函数,这个函数是由Collectors收集器类调用的,而不是Stream流对象。
Map<K,List<T>>有两个泛型,第一个泛型是组的类型,第二个是组内的元素集合类型。实例中按照部门名称分组,所以K的类型是String 类型;部门内的元素是员工集合,所以List集合泛型T的类型就应该是Employee类型

例题14.29

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;public class GroupingDemo2 {public static void main(String[] args) {List<Employee> list = Employee.getEmpList(); // 获取公共类的测试数据Stream<Employee> stream = list.stream(); // 获取集合流对象// 一级分组规则方法,按照员工部门进行分级Function<Employee, String> deptFunc = Employee::getDept;// 二级分组规则方法,按照员工部门进行分级Function<Employee, String> sexFunc = Employee::getSex;// 将流中的数据进行二级分组,先对员工部分进行分组,在对员工性别进行分组Map<String, Map<String, List<Employee>>> map = stream.collect(Collectors.groupingBy(deptFunc, Collectors.groupingBy(sexFunc)));// 获取Map的中的一级分组键集合,也就是部门名称集合Set<String> deptSet = map.keySet();for (String deptName : deptSet) { // 遍历部门名称集合// 输出部门名称System.out.println("【" + deptName + "】 部门的员工列表如下:");// 获取部门对应的二级分组的Map对象Map<String, List<Employee>> sexMap = map.get(deptName);// 获得二级分组的键集合,也就是性别集合Set<String> sexSet = sexMap.keySet();for (String sexName : sexSet) { // 遍历部门性别集合// 获取性别对应的员工集合List<Employee> emplist = sexMap.get(sexName);System.out.println("    【" + sexName + "】 员工:"); // 输出性别种类for (Employee emp : emplist) {// 遍历员工集合System.out.println("        " + emp); // 输出对应员工信息}}}}
}

运行结果如下:

难点:
实例中两个 groupingBy0方法的参数不一样,一个是 groupingBy(性别分组规则),另一个是groupingBy(部门分组规则,groupingBy(性别分组规则))。
获得的Map 对象中,还嵌套了Map对象,它的结构是这样的:Map<部门,Map<性别,List<员工>>>
从左数,第一个Map对象做了一级分组,第二个Map对象做了二级分组。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/112074.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

虚拟机如何联网【NAT】

查看VMWARE的IP地址 #进入root用户 su -#更改虚拟网卡设置界面 vi /etc/sysconfig/network-scripts/ifcfg-ens33 修改ONBOOT为yes BOOTPROTO为static IPADDR为前面的网段 192.168.211.xx (xx为自己设置的&#xff0c;可以随意设置&#xff0c;前面的为前面查看的IP地址的前…

Vue2之防抖_debounce封装函数v-debounce自定义指令(传参/不传)

目录 1、防抖 2、debounce - 封装函数 3、v-debounce 全局自定义指令 1、防抖 推荐文章 &#xff1a; https://blog.csdn.net/weixin_58099903/article/details/119902796 2、debounce - 封装函数 utils / tools.js /*** 函数防抖 是n秒后延迟执行&#xff0c;多用于页面scr…

MATLAB——神经网络参考代码

欢迎关注“电击小子程高兴的MATLAB小屋” %% I. 清空环境变量 clear all clc %% II. 训练集/测试集产生 %% % 1. 导入数据 load spectra_data.mat %% % 2. 随机产生训练集和测试集 temp randperm(size(NIR,1)); %打乱60个样本排序 % 训练集——50个样本 P_train NIR(…

YOLOv8改进实战 | 更换主干网络Backbone之轻量化模型Efficientvit

前言 轻量化网络设计是一种针对移动设备等资源受限环境的深度学习模型设计方法。下面是一些常见的轻量化网络设计方法: 网络剪枝:移除神经网络中冗余的连接和参数,以达到模型压缩和加速的目的。分组卷积:将卷积操作分解为若干个较小的卷积操作,并将它们分别作用于输入的不…

机器学习基础之《回归与聚类算法(3)—线性回归优化:岭回归》

一、什么是岭回归 其实岭回归就是带L2正则化的线性回归 岭回归&#xff0c;其实也是一种线性回归。只不过在算法建立回归方程时候&#xff0c;加上L2正则化的限制&#xff0c;从而达到解决过拟合的效果 二、API 1、sklearn.linear_model.Ridge(alpha1.0, fit_interceptTrue…

【无人机】太阳能伪卫星VoLTE无人机设计(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

从0到1,申请cos服务器并上传图片到cos文件服务器

目录 准备工作 Java代码编写 控制台打印 整理成工具类 编写接口 Postman测试 准备工作 1.进入网址腾讯云 产业智变云启未来 - 腾讯 (tencent.com) 2.搜索cos,点击立即使用&#xff0c;刚开始会免费赠送你 3.存储都是基于桶的&#xff0c;先创建桶&#xff0c;在桶里面创…

PyQt 问题记录

1.现成的组件不一定线程安全&#xff0c;&#xff08;包括且不限于数据的修改竞争,和一些组件的崩溃 ) 对于PyQt 的线程使用&#xff0c;可能还需要更谨慎些 保存逻辑 QuestionBox("保存/Save")def Save(self):okFlagFalseerrFlagFalseWriteCmd{}for it in self.Mode…

视频SDK开发,多平台SDK快速接入

随着科技的不断发展&#xff0c;视频已经成为了企业业务中不可或缺的一部分。无论是在线教育、企业培训还是产品展示&#xff0c;视频都发挥着至关重要的作用。为了满足企业对视频应用的需求&#xff0c;美摄视频SDK应运而生&#xff0c;为企业提供了一站式的视频解决方案。 一…

从入门到进阶 之 ElasticSearch 节点配置 集群篇

&#x1f339; 以上分享 ElasticSearch 安装部署&#xff0c;如有问题请指教写。&#x1f339;&#x1f339; 如你对技术也感兴趣&#xff0c;欢迎交流。&#x1f339;&#x1f339;&#x1f339; 如有需要&#xff0c;请&#x1f44d;点赞&#x1f496;收藏&#x1f431;‍&a…

回顾 | E³CI效能认知与改进论坛,助力企业研发效能度量和提升

2023年8月&#xff0c;TiD质量竞争力大会组委会和ECI专家委员会成功举办TiD大时段课程“度量驱动研发效能提升”与“ECI效能认知与改进论坛”。与会专家以《ECI软件研发效能度量规范》团体标准为要点&#xff0c;为企业研发效能度量和提升分享诸多实践成果与经验。 《ECI软件研…

es6(三)——常用es6(函数、数组、对象的扩展)

ES6的系列文章目录 第一章 Python 机器学习入门之pandas的使用 文章目录 ES6的系列文章目录0、数值的扩展一、函数的扩展1、函数的默认值2、函数的reset参数 二、数组的扩展1. 将对象转成数组的Array.from()2. 将对象转成数组的Array.from()3. 实例方法 find()&#xff0c;fin…

高质量发展新引擎:智能工业操作系统助力产业升级

工业操作系统是推动制造业高质量发展的重要支撑&#xff0c;也是推动经济发展方式转变的重要手段。打造自主可控的工业实时操作系统是实现工业科技自立自强的必然要求&#xff0c;是推动高质量发展的必由之路。 基于软件定义控制的智能工业操作系统 Intewell操作系统是由科东…

uniapp高德地图ios 使用uni.chooseLocation选取位置显示没有搜索到相关数据

uniapp云打包后&#xff0c;高德地图ios选取位置显示“ 对不起&#xff0c;没有搜索到相关数据” 详细问题描述 废话不多说&#xff0c;直接上图 解决方案 1.打开高德地图开发平台 2.重新创建key 3.获取云打包时的ios报名作为安全码 4.使用生成的高德key更改manifest.json里…

Ubuntu系统忘记Root用户密码-无法登录系统-更改Root密码-Ubuntu系统维护

一、背景 很多时候&#xff0c;我们总会设计复杂的密码&#xff0c;但是大多数时候&#xff0c;我们反而会先忘记我们的密码&#xff0c;导致密码不仅仅阻挡其他用户进入系统&#xff0c;同时也阻碍我们进入系统。 本文将介绍在忘记密码的情况下&#xff0c;如何进入系统并更改…

分析并实现Android中的MVC、MVP架构模式

架构是什么 架构是为了解决特定的问题而提出来的&#xff0c;而且它还有特定的规则&#xff0c;能够把整个应用的整体进行角色的划分。并且他还能够约定角色之间的联系沟通机制。 所以学习架构要带着以下三个问题去理解&#xff1a; 。架构解决了什么问题&#xff1f; 。架…

AUTOSAR AP硬核知识点梳理(1)

一 什么是 Adaptive AUTOSAR? Adaptive AUTOSAR是一种新的汽车软件框架,旨在满足现代汽车行业中不断增长的技术需求。随着汽车变得越来越智能,对处理器的性能要求也在不断增长。 Adaptive AUTOSAR旨在通过提供高性能计算和通信机制以及灵活的软件配置来满足这些需求,为车…

软件工程与计算总结(二十)软件交付

软件交付是软件项目的结束阶段 &#xff0c;标志着软件开发任务的完成——其作为一个分水岭&#xff0c;区分了软件开发与软件维护两个既连续又不同的软件产品生存状态~ 在经历连续的辛苦工作之后&#xff0c;开发人员在胜利曙光之前难免会忽视软件交付阶段的一些工作——在准…

踩坑记 BSS段的初始化

title: 踩坑记 BSS段的初始化 category_bar: true categories: blog tags:embedded date: 2023-10-20 19:23:05 前言 接手一个项目&#xff0c;调试全靠串口日志&#xff0c;测试同事测试产品的时候无法拿到日志&#xff0c;刚好产品RAM够大&#xff0c;且刚好有SD卡。所以就…

Arcgis中像元值变化问题,拉伸显示的是否为实际像元值范围?

Arcgis中合并栅格但像元值变化 问题描述 这是四幅栅格&#xff0c;范围都在-1-9之间&#xff0c;怀疑这个范围是否是真实的范围。因为经常听到同学说放到arcgis拉伸显示之后&#xff0c;值变化了&#xff0c;所以研究一下。 原因 可以打开ENVI的像元快速统计工具&#xff…