第十四章 Lambda表达式

目录

Iambda表达式简介

1.函数式接口

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

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

4.lambda表达式使用代码块

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

Iambda表达式与异常处理

14.2方法的引用

引用静态方法

引用成员方法

引用构造方法

1.引用无参构造方法

2.引用有参构造方法

3.数组构造方法

14.2.5Fuction接口 

14.3流处理

14.3.2 Optional类

14.3.3 Collectors类 

14.3.4 数据过滤 

1. filter()方法

2. distinct()方法 

3. Iimit()方法

4. skip方法 

14.3.5数据映射 

14.3.6 数据查找

1.allMatch()方法

2.anyMatch方法()方法

3.noneMatch()方法

4.findFirst()方法

14.3.7 数据收集

1.数据统计


Iambda表达式简介

  1. lambda表达式可以用非常少的代码实现抽象方法。
  2. lambda表达式不能独立执行,因此必须实现函数式接口,并且会返回一个函数式接口的对象。
  3. 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.1
		String 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.3
	String check(int grade);    //查询成绩结果
}
public class GradeDemo {
 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		CheckGrade g =(n)-> {			//lambda表达式实现代码块
			if (n>=90&& n<=100) {		//如果成绩在90~100
				return"成绩为优";			//输出成绩为优
			}else if (n>=80&& n<=90) {	//如果成绩在80~90
				return"成绩为良";			//输出成绩为良
			}else if (n>=60&& n<=80) {	//如果成绩在60~80
				return"成绩为中";			//输出成绩为中
			}else if (n>=0&& n<=60) {	//如果成绩在00~60
				return"成绩为差";			//输出成绩为差
			}else {						//其他数字不是有效成绩
				return"成绩无效";			//输出成绩无效
	}
};
	System.out.println(g.check(89));
	}
}

 运行结果如下:

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

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

例题14.4

interface VariableInterface1{		//例题14.4
	void method();
}
public class VariableDemo1 {
	
 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int 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.7
	int 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 stub
		StaticMethodInterface 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 stub
		InstanceMethodDemo demo = new InstanceMethodDemo();			//创建类对象
		InstanceMethodInterface im = demo::format;					//引用类对象的方法
		Date date = new Date();										//创建日期对象
		System.out.println("默认格式:"+date);							//输出日期对象默认格式
		System.out.println("接口输出的格式:"+im.method(date));			//输出经过接口方法处理过的格式
	}
 
}

运行结果如下:

引用构造方法

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 接口还提供
方法以方便开发者对函数逻辑进行更深层的处理。

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;
	}
	
	@Override
	public 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

点击右边的Select Getters 

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

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循环到10
		list.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.17
	public 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.18
	static 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 stub
		List<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("去重后:",result);
	}
 
}

 运行结果如下:

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.19
		public 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.14
 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
			List<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();           // 获取集合流对象
        // 判断所有员工的年龄是否都大于25
        boolean 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();         // 获取集合流对象
        // 判断员工是否有的年龄大于等于40
        boolean 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 stub
		List<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对象做了二级分组。

猜你喜欢

转载自blog.csdn.net/2301_76549195/article/details/133907639
今日推荐