Lambda表达式
作用: 简化匿名内部类对象使用前提: 函数式接口
函数式接口: 只有一个必须被重写的抽象方法的接口称为函数式接口
检查函数式接口注解: @FunctionalInterface 强制检测语法:
() -> {}
() : 要重写的抽象方法的参数列表
-> : 箭头函数 lambda符号 ,具有上下文推到的作用
{} : 要重写抽象方法的方法体
四大内置函数式接口 必须掌握
消费型接口 Consumer<T>
void accept(T t) 有一个参数没有返回值函数型接口 Function<T,R>
R apply(T t) 有一个参数一个返回值段言型接口 Predicate<T>
boolean test(T t) 一个参数一个boolean返回值供给型接口 Supplier<T>
T get() 没有参数一个返回值
消费型接口 Consumer<T>:
public class Class001_FuntionalInterface {
public static void main(String[] args) {
testConsumer(20000,d-> System.out.println("今天给主播托马斯刷4个藏宝图消费"+d));
testConsumer(10000,d-> System.out.println("今天出去喝酒消费"+d));
}
//消费
public static void testConsumer(double money, Consumer<Double> com){
com.accept(money);
}
}
运行结果:
今天给主播托马斯刷4个藏宝图消费20000.0
今天出去喝酒消费10000.0
函数型接口 Function<T,R>
public class Class001_FuntionalInterface {
public static void main(String[] args) {
System.out.println(strHandler(" haha",s->s.trim()));
System.out.println(strHandler(" haha",s->s.toUpperCase()));
//[5,10]
}
//对于某个字符串进行某种行为的操作,得到结果
public static String strHandler(String str, Function<String,String> fun){
return fun.apply(str);
}
}
运行结果:
haha
HAHA
供给型接口 Supplier<T>
public class Class001_FuntionalInterface {
public static void main(String[] args) {
System.out.println(testSupplier(5,()->(int)(Math.random()*(10-5+1)+5)));;
}
//生成n个随机数
public static List<Integer> testSupplier(int num, Supplier<Integer> sup){
List<Integer> list = new ArrayList<>();
for(int i=1;i<=num;i++){
list.add(sup.get());
}
return list;
}
}
运行结果:
[6, 8, 9, 5, 6]
段言型接口 Predicate<T>
public class Class001_FuntionalInterface {
public static void main(String[] args) {
List<String> list1=new ArrayList<>();
list1.add("abcd");
list1.add("ab");
list1.add("abc");
list1.add("a");
List<String> list2 = testPredicate(list1, x -> x.length() <= 3);
System.out.println(list2);
}
//对集合中的字符串数据做某种要求的过滤
public static List<String> testPredicate(List<String> list, Predicate<String> pre){
List<String> ls = new ArrayList<>();
for(String s:list){
if(pre.test(s)){
ls.add(s);
}
}
return ls;
}
}
运行结果:
[ab, abc, a]
方法引用:(了解就可)
方法引用 : 当lambda体{}的实现,已经有另外的方法实现了,可以通过方法引用来引用那个方法(引用|调用)
方法引用可以理解为: lambda表达式的另外一种表现形式
方法引用是用来简化lambda表达式要求:1)lambda体的实现,是通过调用|引用另外一个方法实现的
2)引用的方法的参数列表与返回值要求与要重写的抽象方法的参数列表与返回值保持一致,自动匹配
3)如果返回值匹配,但是参数列表不完全匹配,可以考虑是否满足第三种使用方式: 使用 类名::成员方法名 简化
如果参数列表只有一个,这个参数作为内部调用方法的对象存在
如果参数列表存在多个,第一个参数作为内部调用方法的对象,第二个参数开始自动匹配内部所引用方法的参数列表使用方式:
方法归属者::方法名 静态方法的归属者为类名,普通方法归属者为对象
对象::成员方法名 : 对象真实存在
类名::静态方法名
类名::成员方法名 : 对象自动匹配,对象是lambda的参数提供的
public class Class002_FuncrtionQuite {
public static void main(String[] args) {
test3();
System.out.println("-----");
test2();
System.out.println("-----");
test1();
}
//类名::成员方法名
public static void test3(){
BiPredicate<String,String> biPredicate = (x,y)->x.equals(y);
//分析: 1)lambda体是否是通过调用了另外一个 方法实现的 equals
//2)如果参数列表存在多个,第一个参数作为内部调用方法的对象,
//第二个参数开始自动匹配内部所引用方法的参数列表
biPredicate = String::equals;
System.out.println(biPredicate.test("zhangsan","zhangsan2"));;
}
//类名::静态方法名
public static void test2(){
BiFunction<Integer,Integer,Integer> biFunction = (x,y)-> Math.max(x,y);
//分析: 1)lambda体的实现是通过调用了另一个方法实现的,Math.max()
//2)lambda的参数列表与返回值刚好匹配到内部所调用的max()方法的参数列表与返回值
biFunction =Math::max;
System.out.println(biFunction.apply(101,102));
}
//对象::成员方法名
public static void test1(){
/*Consumer com = o ->{
System.out.println(o);
};*/
PrintStream ps = System.out;
/* Consumer com = o ->{
ps.println(o);
};*/
//分析:
//1)lambda体的实现是通过调用了println方法实现的
//2)抽象方法accept参数列表与返回值类型匹配到println方法参数列表与返回值
//--> 方法引用简化
Consumer com = ps::println;
com.accept("你好");
//List<Integer> ls = List.of(1,2,3,4,5);
List<Integer> ls=new ArrayList();
ls.add(1);
ls.add(2);
ls.add(3);
ls.add(4);
ls.add(5);
ls.forEach(System.out::println);
}
}
运行结果:
false
-----
102
-----
你好
1
2
3
4
5
方法引用 ::
构造器引用
数据类型::new
要求: lambda参数列表与构造器的参数列表保持一致数组引用
类型::new
public class Class003_FuncrtionQuite {
public static void main(String[] args) {
//构造器引用
Function<String,String> fun = (s)->new String(s);
fun = String::new;
System.out.println(fun.apply("abc"));
Supplier<Employee> sup = () -> new Employee();
sup = Employee::new;
System.out.println(sup.get());
//数组 引用
Function<Integer,int[]> sup2 = (i)->new int[i];
sup2 = int[]::new;
System.out.println(sup2.apply(3));
System.out.println(Arrays.toString(sup2.apply(3)));
}
}
运行结果:
abc
Employee{id=0, name='null', age=0, salary=0.0}
[I@4dd8dc3
[0, 0, 0]
Stream操作(重点)
Stream :
数据的渠道,用来操作由数据源(数组,集合)产生元素序列
数组|集合关注数据的存储,Stream关注的是对数据的计算可以对根据数据源锁产生的数据一系列式的流式计算
特点:
1.Stream本身不能存储数据
2.Stream不会修改原对象|数据源中的数据,在每次进行流式操作后会返回一个持有结果的新的流
3.惰性加载|延迟执行: 在进行stream操作时候不会马上执行,会在获取结果时一起执行
4.流式一次性的流,流是不能重复使用,因为这个流已经被使用消费了创建流的方式:
1. Collection中的成员stream()
2. Arrays.stream(数组)
3.Stream.of(数据)
使用Stream的过程:
1.获取流
2.一系列流式的中间操作 (返回持有结果的新的流)
3.终止行为(返回真实结果)中间操作 : 筛选与切片
filter: 过滤
limit:限制从流中获得前n个数据
distinct:去重
skip:跳过前n个数据
public class StreamTest2 {
public static void main(String[] args) {
List<Employee> list=new ArrayList<>();
list.add(new Employee(1001,"zhangsan",18,20000));
list.add(new Employee(1002,"lisi",25,18000));
list.add(new Employee(1003,"wangwu",30,15000));
list.add(new Employee(1004,"zhaoliu",28,10000));
list.add(new Employee(1004,"zhaoliu2",28,10000));
list.add(new Employee(1004,"zhaoliu",28,10000));
//1.获取流
Stream<Employee> stream = list.stream();
//2.中间操作
stream = stream.filter(x -> x.getAge() > 18)
.distinct() //要重写haseCode和equals方法
.limit(3) //限制从流中获得前n个数据
.skip(2); //跳过前n个数据
//终止行为
stream.forEach(System.out::println);
}
}
输出结果
Employee{id=1004, name='zhaoliu', age=28, salary=10000.0}