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

lambda 表达式

lambda 表达式简介

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

可以将lambda表达式语法用法如下:

()-> 结果表达式

参数->结果表达式

(参数1,参数2,...,参数n)结果表达式

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

 语法格式如下:

()->{代码块 

参数 ->{代码块}  

(参数1,参数2,...,参数n)->{代码块 }  

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

 lambda 表达式的语法非常抽象,并且有着非常强大的自动化功能,如自动识别泛型、自动数据类型转换等,这会让初学者很难掌握。如果将 lambda 表达式的功能归纳总结,可以将 lambda 表达式语法用如下方式理解:

()                ->                {代码块}

这个方法       按照           这样的代码来实现

lambda 表达式实现函数式接口

1、函数式接口

       函数式接口指的是仅包含一个抽象方法的接口,接口中的方法简单明了地说明了接口的用途。开发者可以创建自定义的函数式接口,例如:

 interface SayHi{
    String say();
}

2、lambda 表达式实现无参抽象方法

很多函数式接口的抽象放方法式无参数的这样的无参抽象方法在lambda表达式中使用“()”表示。

代码如下:

interface SayHi{
	String say();
}
public class NoParaDemo {
	public static void main(String[] args) {
		SayHi sh1=new SayHi() {
			public String say() {
			return "这里是匿名内部类";
		}	
	};
	System.out.println(sh1.say());
	
	//通过lambda表达式补全方法体
	SayHi sh2=()-> {
		return "这里是lambda表达式";
};
System.out.println(sh2.say());
	}
}

 运行结果如下:

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

抽象方法中有一个或多个参数的函数式接口也是很常见的,lambda 表达式中可以用“(a1,a2,a3)”的方法表示有参数抽象方法,圆括号里标识符对应抽象方法的参数。如果抽象方法中只有一个参数,则可以省略圆括号。代码如下:

interface AddInt {
	int add(int a,int b);	
}
public class ParamDemo {
	public static void main(String[] args) {
			//使用匿名内部类补全方法体
		AddInt ail=new AddInt() {
			public int add(int a,int b) {
				return a+b;
			}
		};
		System.out.println("匿名内部类:"+ail.add(3, 5));	
		
		//使用lambda表达式补全方法体
		AddInt ai2=(a,b)-> {
				return a+b;	
		};
		System.out.println("lamb表达式:"+ai2.add(3, 5));	
	}
}

运行结果如下:

4.lambda表达式使用代码块

当函数式接口的抽象方法需要实现复杂逻辑而不是返回一个简单的表达式的话,最需要在lmbd
表达式中使用代码块。lambda表达式会自动判断返回值类型是否符合抽象方法的定义。

package sx;
 
	interface CheckGrade{
		String check(int grade);	//查询成绩结果
	}
public class Check {
	public static void main(String[] args) {
		CheckGrade g = (n)->{	//lambda表达式实现代码块
			if(n>90 && n<=100) {	//如果成绩在90~100
				return"成绩为优";		//输出成绩为优
			}else if(n>=80&& n<90) {	//如果成绩在80~89
				return"成绩为良";		//输出成绩为良
			}else if(n>=60&& n<80) {	//如果成绩在60~79
				return"成绩为中";		//输出成绩为中
			}else if(n>=0&& n<60) {		//如果成绩小于60
				return"成绩为差";		//输出成绩为差
			}else {					//其他数字不是有效成绩
				return " 成绩无效";	//输出成绩无效
			}
		};							//不要丢掉lambda语句后的分号
		System.out.println(g.check(89));	//输出查询结果
 
	}
 
}

运行结果如下:

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

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

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

interface Variable{//测试接口
	void method();//测试方法
	}
 
public class VariableDemo {//测试类
	public static void main(String[] args) {
		int value=100;//创建局部变量
		Variable v=()->{//实现测试接口
			int num=value-90;//使用局部变量赋值
			value=12;//更改局部变量,此处会报错,无法通过编译
		};
	}
}

在编写玩这段代码后,会看到更改局部变量的相关代码被标注编译错误

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

       类成员变量是在lambda 表达式中不是被final修饰的,所以lambda 表达式可以改变其值。代码如下:

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

运行结果如下:

lambda 表达式与异常处理

       很多接口的抽象方法为保证程序的安全性,会在定义时就抛出异常。但是lambda 表达式中并没有抛出异常的语法,这是因为lambda 表达式会默认抛出抽象方法原有的异常,当此方法被调用时则需要进行异常处理。代码如下:

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

 运行结果如下:

 

方法的引用

引用静态方法

引用静态方法的语法如下:

类名::静态方法名

interface AddInt {//测试接口
	int add(int a,int b);//抽象方法
}
public class ParamDemo {
	static int add(int a,int b) {//静态方法,返回两个参数相加的结果
		return a+b;//返回相加结果
	}
public static void main(String[] args) {
	//使用方法的引用补全方法体
		AddInt ai3=ParamDemo::add;//引用ParamDemo类的静态方法
		System.out.println("方法的引用:"+ai3.add(3, 5));//输出结果
	}
}

 运行结果如下:

引用成员方法

引用成员方法的语法如下:

对象名::成员方法名

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

 运行结果如下:

引用带泛型的方法

      泛型是Java开发经常使用到的功能,“::”操作支持引用带泛型的方法。除方法外,“::”操作符也支持引用带泛型的类。

引用构造方法

1、引用无参数构造方法

引用无参数构造方法的方法如下:

类名::new

interface ConstructorsInterface{
	ConstructorsDemo action();
}
 
public class ConstructorsDemo {
	public ConstructorsDemo(){
	System.out.println("无参构造方法");
	}
 public static void main(String[] args) {
	  ConstructorsInterface ci=ConstructorsDemo::new;
	  ci.action();
}
}

运行结果如下:

2、 引用有参数构造方法

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

interface ConstructorsInterface{
	ConstructorsDemo action(int a);
}
public ConstructorsDemo(int a){
		System.out.println("有参构造方法"+a);
		}
  public static void main(String[] args) {
	  ConstructorsInterface ci=ConstructorsDemo::new;
	  ci.action(8);
}
}

运行结果如下:

流处理

     流处理有点类似数据库的SQL语句,可以执行非常复杂的过滤、映射、查找和收集功能,并且代码量很少。唯一的缺点是代码可读性不高,如果开发者基础不好,可能会看到API所表达的含义。

Stream 接口介绍

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

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

List<Integer>list=new ArrayList<Interger>();     //创建集合

Stream<Integer> s=list.stream();       //获取集合流对象

Collectors 类

 Collectors 类为收集器类,该类实现了 java.util.Collector 接口,提供了非常丰富的 API,有着强大的数据挖掘能力,也可以将 Stream 流处理进行各种各样的封装、归类、分组等操作。同时,Collectors 类还提供了很多实用的数据加工方法,如数据统计计算等。

数据过滤 

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

1、filter() 方法

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

2、limit() 方法

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

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;
	}
	
	//重写toString()方法,方便打印员工信息
	public String toString() {
		return "Employee [name=" + name + ", age=" + age + ", salary=" + salary + ", sex=" + sex + ", dept=" + dept
				+ "]";
	}
	
	//以下是员工属性的getter方法
	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>getEmployee(){//提供数据初始化方法
	List<Employee>list=new ArrayList<Employee>();
	list.add(new Employee("老张",40,9000,"男","运营部"));//添加员工数据
	list.add(new Employee("小刘",24,5000,"女","开发部"));
	list.add(new Employee("大刚",32,5000,"男","销售部"));
	list.add(new Employee("翠花",28,7500,"女","销售部"));
	list.add(new Employee("小李",21,3000,"男","开发部"));
	list.add(new Employee("老王",35,6000,"男","人事部"));
	list.add(new Employee("小王",21,3000,"男","人事部"));
	return list;
	}
}
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
public class StreamDemo {
 
	public static void main(String[] args) {
	List<Employee> list=Employee.getEmployee();
	Stream<Employee>stream=list.stream();
	//筛选年龄大于30的员工
	Stream<Employee>str30=stream
			.filter(people->people.getAge()>30)
			.limit(3);//限制条数
	List<Employee>result=str30.collect(Collectors.toList());
	for(Employee emp:result) {
		System.out.println(emp);
	}
	}
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
public class GroupDemo {
	public static void main(String[] args) {
		List<Employee> list=Employee.getEmployee();//获取公共类的测试类
		Stream<Employee>stream=list.stream();	//获取集合流对象
		
//按照部门分成若干List集合,集合中保存员工对象,返回成Map对象
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();
		}
	}
 
}

 运行结果如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值