我是靠谱客的博主 深情飞机,这篇文章主要介绍Lambda表达式详细讲解---满满的干货哦Lambda 表达式,现在分享给大家,希望可以做个参考。

Lambda 表达式详解

  • Lambda 表达式
    • lamdba 简介:
    • 函数式接口:
    • Lambda表达式需注意的点:
    • Lambda 表达式语法示例:
    • Java 预定义函数式接口:
    • 函数式数据处理:lambda表达式和预定义函数式接口的应用。
    • 总结:

Lambda 表达式

lamdba 简介:

lambda 是Java 8 推出的,它是函数式接口,并不是增强‘语法糖’的匿名类。什么是函数式接口呢?这就得先说一下面向接口编程。在日常开发中面向接口编程不仅降低了耦合,还增加了代码的灵活性和复用性。在传统的面向接口编码时,大多功能方法都是接受接口类型的参数,但它实际要的时接口的行为方法。但是没法传递方法本身,只能传递接口。通过接口来传递行为代码,这时就需要一个实际对象,所以匿名类的优势就出来了。而lambda表达式就是为了简化匿名类 这种方式而产生的。既然接受接口参数,是为了传递行为代码,那现在java 就直接引出了一个新的概念叫函数式接口来优化传统的编码方式。函数式接口只有一个抽象方法,之所以强调是一个抽象 方法,是因为 Java 8 之后还可以允许定义静态方法和默认方法。

函数式接口:

通过@FunctionnallInterface 注解来声明这是一个函数式接口,其实不加这个注解也是可以的,只要接口只有一个抽象方法,也会被认为是函数式接口。加上注解是为了清晰的告诉使用者,这是一个函数式接口,同时这个注解也和@Override 注解作用一样,在编译的 适合会检查该注解下的接口是否只有一个抽象方法,如果不止一个,就会编译报错。

Lambda表达式需注意的点:

lambda 表达式不会生成匿名类,它不像之前传递行为接口方法那样,会生成一个匿名类。同时它是利用Java 7 引入的动态类型语言中指令和方法句柄来实现的 ,非常高效。

Lambda 表达式语法示例:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
```package lambda; import java.io.File; import java.io.FilenameFilter; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * java8 推出的lambda 表达式 *lambda 表达式不是内部类,它是函数式接口 *函数式接口只能有一个抽象方法,之所以强调是抽象方法是因为8之后还允许定义静态和默认方法 * @FunctionnallInterface 注解清晰告诉使用者这是一个函数式接口,不加这个注解也行,加了之后超过一个抽象方法编译时会报错 * *lambda 表达式不会生成类,它不像之前的传递行为接口方法那样,会生成一个匿名内部类,它是利用了Java 7引入的支持 *动态类型语言引入的指令和方法句柄来实现的,而且很高效 */ public class LambdaDemo { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(100); //这是传统写法,void execute(Runnable command);这是面向接口编程,降低了程序的耦合,提高灵活性,复用性。 //很多时方法中接受的参数不是接口,而是接口的方法,但没法直接传递方法,只能传递接口。通过接口传递行为代码, //就需要接口的一个实际对象,所以用匿名类的方式是最简洁的。而lambda 表达式可以简化代码。 executorService.execute(new Runnable() { @Override public void run() { System.out.println("hello word!"); } }); String msg = "Hello word!"; //Lambda 表达式和匿名类不同的地方是,lambda表达式可以访问局部变量,不要求变量要声明为final //但是不允许局部变量重新赋值,因为java会把参数传给lambda表达式,为lambda 创建一个副本,它的代码 //内部访问这个副本,而不是外部的变量,如果允许被修改,会让程序员误认为lambda表达式读的是后面的修改的值 //给lambda创建副本是因为局部变量定义在栈中,当lambda表达式执行的时候,局部变量可能早就被释放了, //如果希望能修改值,可以将变量定义为实例变量,或者数组、集合 executorService.submit(() -> System.out.println(msg.toString())); //列出当前目录下所有扩展名为 .text File f = new File("."); File [] files = f.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { if(name.endsWith(".txt")){ return true; } return false; } }); //lambda 表达式 第一个版本 简化上面代码 File [] files1 = f.listFiles((File dir,String name) ->{ if(name.endsWith(".txt")){ return true; } return false; }); //lambda 表达式 第二个版本 lambda 表达式是由 -> 分隔成两个部分,前面是参数,后面是方法块 File [] files2 = f.listFiles((File dir,String name) -> { return name.endsWith(".txt"); }); //lambda 表达式 第三个版本 当方法内部只有一条语句时,{}和return 可以省略,这时主代码是一个表达式,不能加;和return File [] files3 = f.listFiles((File dir,String name) -> name.endsWith(".txt") ); //lambda 表达式 第四个版本 方法的参数类型声明也是可以省略的,因为java可以自动推断出来,它知道listFiles() //接受的参数是 FilenameFilter接口,该接口只有一个accept方法,这个方法的两个参数类型分别是File和String File [] files4 = f.listFiles((dir,name) -> name.endsWith(".txt")); //lanbda 表达式 第五个版本 当方法参数没有时 直接可以双括号() executorService.execute(() -> System.out.println("lambda say hello")); //lanbda 表达式 第六个版本 只有一个参数时,()可以省略,直接写参数 File [] files5 = f.listFiles(pathname -> pathname.getName().endsWith(".txt")); } }

Java 预定义函数式接口:

函数式接口方法定义说明
Predicateboolean test(T t)谓词,测试输入是否满足条件
Function<T,R>R apply(T t)函数转换,输入类型T,输出
Consumervoid accept(T t)消费者,输入类型T
SuplierT get()工厂方法
UnaryOperatorT apply(T t)函数转换的特例,输入和输出类型一样
BiFunction<T,U,R>R apply(T t,U u)函数转换,接受两个参数,输出R
BinaryOperatorT apply(T t,T u)BiFunction 的特例,输入输出类型都一样
BiConsumer<T,U>void accept(T t,U u)消费者,接受两个参数
BiPredicate<T,U>boolean test(T t ,U u) 谓词,接受两个参数

对于基本类型boolean,int,long,double ,为避免装箱和拆箱,java 8 也提供了一些专门的函数,下面列举int的预定义函数接口。

int类型的函数式接口:

函数式接口方法定义说明
IntPredicateboolean test(int value)谓词,测试输入是否满足条件
IntFunctionR apply(int value)函数转换,输入类型int,输出类型R
IntConsumervoid accept(int value)消费者,输入类型int
IntSupplierint getAsInt()工厂方法
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package lambda; import annotation.Label; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; /** * java 8 预定义的函数式接口,用于常见类型的代码传递 * * 1.Predicate<T> boolean test(T t) 谓词,测试输入是否满足条件 * 2.Function<T,R> R apply(T t) 函数转换,输入类型T,输出类型R * 3.Consumer<T> void accept(T t) 消费者,输入类型T * 4.Suplier<T> T get() 工厂方法 * 5.UnaryOperator<T> T apply(T t) 函数转换的特例,输入和输出类型一样 * 6.BiFunction<T,U,R> R apply(T t,U u) 函数转换,接受两个参数,输入R * 7.BinaryOperator<T> T apply(T t,T u) BiFunction的特例,输入输出类型都一样 * 8.BiConsumer<T,U> void accpet(T t,U u) 消费者,接受两个参数 * 9.BiPerdicate<T,U> boolean test(T t, U u) 谓词,接受两个参数 * * 对于基本类型boolean,int,long,double ,为避免装箱和拆箱,java 8 也提供了一些专门的函数 * eg: */ public class ReserveFunction { static class Student { String name; double score; public Student(String name, double score) { this.name = name; this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } } //Predicate<T> 预定义函数谓词用法,这是定义了一个通用的过滤方法 public static <T> List<T> filter(List<T> list, Predicate<T> predicate){ List<T> retList = new ArrayList<>(); for (T t : list) { if(predicate.test(t)){ retList.add(t); } } return retList; } //Function<T,R> 预定义函数函数转换用法,这是定义了一个通用的转换方法 public static <T,R> List<R> map(List<T> list, Function<T,R> function){ List<R> retList = new ArrayList<>(); for (T t: list) { retList.add(function.apply(t)); } return retList; } //Consumer示例,预定义函数消费者用法,这是定义一个通用的消费者方法 public static <T> void foreach(List<T> list, Consumer<T> consumer){ for (T t : list) { consumer.accept(t); } } public static void main(String[] args) { List<Student> list = Arrays.asList(new Student [] {new Student("zhangsan",90d), new Student("lisi",89d),new Student("wangwu",98d)}); //filter() 接收的是一个Predicate<T>的预定义函数式接口,该函数表达式的方法是test(T t), //t -> t.getScore() >= 90 就是predicate<T> 接口 test(T t) 的实现 //所以Lambda 表达式可以简化如下: list = filter(list, t -> t.getScore() >= 90); for (Student t: list) { System.out.println(t.getName()); } //Function<T,R> apply(T t),student -> student.getName() 这是apply() 的实现 //这是将学生集合转为学生名字集合 List<String> nameList = map(list,student -> student.getName()); for (String name:nameList) { System.out.println(name); } //这是将学生集合,转为学生名字大写后的学生集合 list = map(list, student -> new Student(student.getName().toUpperCase(),student.getScore())); for (Student t: list) { System.out.println(t.getName()); } //上面是生成了新对象,还有一种常见的方式是直接修改原来的对象 foreach(list, student -> student.setName(student.getName().toUpperCase())); for (Student t: list) { System.out.println(t.getName()); } //方法引用 //lambda 表达式经常用于调用某个对象的某个方法 foreach(list, student -> student.setName(student.getName().toUpperCase())); //Java 8 引入新的语法,可以简化,称为方法引用,由 :: 分割为两部分 前面是类名或者变量名,后面是方法名, // 方法可以是静态或者实例方法 foreach(list, Student :: getName); } }

函数式数据处理:lambda表达式和预定义函数式接口的应用。

java 8引入了一套新的类库,java.util.stream下,称为stream API,用于常见的集合数据处理。它是函数式的,非常简介,易读,灵活。它是由接口Stream 下的方法来进行对集合数据的处理。Stream 接口类似一个迭代器,提供了丰富的操作.Java 8 给 Collection 接口增加了两个默认的方法,用来返回一个stream.
示例如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package lambda; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 函数式数据处理:基础用法,lambda表达式和函数式接口的应用 * Java 8引入了一套新的类库,位于包java.util.stream 下,称为 stream API,用于常见的集合数据处理 * 它是函数式的,非常简洁,灵活,易读 * 接口Stream类似一个迭代器,提供了丰富的操作 * java 8给Collection 接口增加两个默认方法,用来返回一个stream */ public class FunctionDataUser { public static void main(String[] args) { List<String> list = new ArrayList<>(Arrays.asList(new String[]{"say", "hello"})); //Collection 接口新增的两个获取stream流的方法 //获取顺序流 stream() Stream orderStream = list.stream(); //获取并姓流,可能背后多个线程并行处理 Stream paralleStream = list.parallelStream(); //Stream api 的操作分为两种: //中间操作:这种是不发生实际触发执行,用于构建流水线,返回Stream的操作。 //终端操作:这种是触发事件操作,返回具体结果的操作。 List<StudentTestCase> testCases = Arrays.asList(new StudentTestCase[]{ new StudentTestCase("xiaozhang", 90d), new StudentTestCase("xiaohuang", 85d), new StudentTestCase("xiaoli", 98d)}); //下面是各种中间操作Api的演示: //1.过滤stream api filter() 方法 //这是传统编码方式 List<StudentTestCase> filterList = new ArrayList<>(); for (StudentTestCase studentTestCase : testCases) { if (studentTestCase.getScore() > 90) { filterList.add(studentTestCase); } } //这是stream filter(Predicate<? super T> predicate) 过滤 api,collect()方法后面讲解 filterList = testCases.stream().filter(t -> t.getScore() >= 90) .collect(Collectors.toList()); for (StudentTestCase studentTestCase : filterList) { System.out.println(studentTestCase.getName()); } //2.基本转换 map(Function<? super T>,? extends R),将学生列表转为学生名字列表 List<String> nameList = new ArrayList<>(); nameList = testCases.stream().map(StudentTestCase::getName).collect(Collectors.toList()); //过滤和转换的组合使用,将90分以上的学生按名字返回,这样代码非常简介和易读, //同时 filter 和 map 一起使用不会遍历两次,而是一次,它们只是在构建操作的流水线, //collect 才是触发实际的遍历执行,在一次遍历中完成过滤、转换以及收集结果的任务 nameList = testCases.stream().filter(t -> t.getScore() > 90) .map(StudentTestCase::getName).collect(Collectors.toList()); //3.distinct 过滤重复数据,它是返回一个新的stream,过滤掉重复的数据,是根据equals()来过滤的。 //filter和map是无状态的,对于流中的每个元素,处理都是独立的,处理后即交给流水线中的下一个操作。 // 而distinct 是有状态的,它会在内部记录之前出现的元素,后面重复的数据就会过滤掉,不传给流水线下一个操作。 //对于顺序流,内部实现时,distinct操作会使用HashSet记录出现过的元素,如果流是有顺序的,需要保留顺序, //会使用LinkedHashSet List<String> list1 = Arrays.asList(new String[]{"say", "happy", "so", "So"}); List<String> ditList = list1.stream().filter(t -> t.length() > 3) .map(String::toUpperCase).distinct().collect(Collectors.toList()); //4.sorted 排序,两个排序方法 sorted() 和 sortde(Comparator<? super T> comparator), //都是返回一个新的stream。sorted 也是一个有状态的中间操作,需要内部记录出现过的元素。与distinct不同的是 //distinct 是每碰到流的一个元素,能立即做出处理,sorted 要先排序,排序就需要保存之前碰到的元素,到流结尾时在排序, //最后将排序的元素逐个传递给流水线的下一个操作。 //比如,过滤得到90分以上的学生,然后按分数从高到低排序,分数一样的按名称排序。 List<StudentTestCase> sList = testCases.stream().filter(t -> t.getScore() > 90) .sorted(Comparator.comparing(StudentTestCase::getScore).reversed() .thenComparing(StudentTestCase::getName)).collect(Collectors.toList()); //5.skip 跳过流中的n个元素。如果流中元素不足n个,返回一个空流。对于前n个元素,skip操作就是过滤,后面的 //元素,skip就是传递给流水线的下一个操作。 //将学生列表按照分数排序,返回第二名以后的 List<StudentTestCase> stList = testCases.stream(). sorted(Comparator.comparing(StudentTestCase::getScore).reversed()).skip(1).collect(Collectors.toList()); //6.limit 限制流的最大长度,limit 不需要处理流中的所有元素,只需要处理元素个数达到限定的maxSize, //后面的元素就不需要处理了,这种可以提前结束的操作叫短路操作 //将学生列表按照分数排序,返回第二名和第三名 List<StudentTestCase> sl = testCases.stream().sorted(Comparator.comparing(StudentTestCase::getScore).reversed()) .skip(1).limit(2).collect(Collectors.toList()); //Java 9 新增了两个相当于skip 和limit 更通用的方法 //7.dropWhile(Predicate<? super T> predicate),通用的skip, // 在谓词返回true的情况下一直进行skip操作,直到某次返回false //得到90分以上的学生 List<StudentTestCase> dList = testCases.stream(). sorted(Comparator.comparing(StudentTestCase::getScore)). dropWhile(studentTestCase -> studentTestCase.getScore() < 90).collect(Collectors.toList()); for (StudentTestCase studentTestCase:dList) { System.out.println("名字: " + studentTestCase.getName() + "分数:" + studentTestCase.getScore()); } //8.takeWhile(Predicate<? super T> predicate),通用的limit,在谓词返回true的情况下一直接受,知道某次返回false //得到90分以上的学生 List<StudentTestCase> tList = testCases.stream(). sorted(Comparator.comparing(StudentTestCase::getScore).reversed()) .takeWhile(t -> t.getScore() >= 90).collect(Collectors.toList()); for (StudentTestCase studentTestCase:tList) { System.out.println("名字: " + studentTestCase.getName() + "分数:" + studentTestCase.getScore()); } //9.peek(Consumer<? super T> action),这方法主要是支持调试,它返回的stream与之前的流是一样的,但它提供了一个 //Consumer,会将流中的每一个元素传给该Comsumer.可以使用该方法观察在流水线中流转的元素,比如: List<String> peekName = testCases.stream().filter(t -> t.getScore() >= 90) .peek(System.out ::print).map(StudentTestCase::getName).collect(Collectors.toList()); for (String name: peekName) { System.out.println(name); } //终端操作,中间操作不触发实际的执行,返回值是Stream,而终端操作触发执行,返回一个具体的值 // 。上面的collect 就不介绍,就是返回一个集合 //1.max(Comparator<? super T> comparator)/min(Comparator<? super T> comparator) // 返回流中的最大值和最小值,它们返回的类型是Optional<T>,而不是T //Optional<T>是java8 引入的新类,它是一个泛型容器类,内部只有一个类型为T的单一变量value, //可能为null,也可能不会null.Optional作用是准确的传递程序的语义。它清楚的表明,其代表的值可能为 //null,程序员应该进行适当的处理 //在max和min的列子中,通过声明我们可以知道具体的返回值不一定存在,这发生在流中不含任何元素的情况下 StudentTestCase studentTestCase = testCases.stream(). max(Comparator.comparing(StudentTestCase::getScore).reversed()).get(); //2.count,返回流中元素的个数。比如,统计大于90分的学生个数 Long count = testCases.stream().filter(t -> t.getScore() > 90).count(); //3.allMatch/anyMatch/noneMatch,都接受谓词Predicate<T>,返回boolean, //用于判断流中的元素是否满足一定的条件。它们的区别是: //.allMatch: 只要在流中所有元素都满足条件的情况下才返回true。 //.anyMatch: 只有流中有一个元素满足条件就返回true。 //.noneMatch: 只有流中所有元素都不满足条件才返回true。 //.如果流为null,那么这几个函数的返回值都是true。 //比如判断有没有满分学生(>=90) boolean good = testCases.stream().anyMatch(t -> t.getScore() == 100); //4.findFirst/findAny,返回值是Optional<T>,如果流为null,返回Optional.empty(). //.findFirst:返回第一个元素 //.findAny 返回任意一个元素 //随便找一个90分以上的学生 Optional<StudentTestCase> optional = testCases.stream().filter(t -> t.getScore() > 90).findAny(); //5.forEach(Consumer<? super T> consumer) 和 forEachOrdered(Consumer<? super> consumer) //遍历操作,都接受Consumer函数式接口,对流中的每一个元素都传递给consumer。不同的是 //在并行流时,foreach 不能保证处理的顺序,foreachOrdered可以保证流中元素的出现顺序进行处理 //逐行打印大于90学生名字 testCases.stream().filter(t -> t.getScore() > 90).map(t -> t.getName()).forEach(System.out :: println); //6.toArray 将流转换为数组,有两个方法 //Object [] toArray() 返回的是Object 数组 //<A> A[] toArray(InFunction<A[]> generator), generator接收的参数是流的元素个数 //它应该返回对应大小的正确类型的数组 //比如,获取学生成绩排名前五的学生 StudentTestCase [] studentTestCases = testCases.stream().filter(t -> t.getScore() > 90) .toArray(t -> new StudentTestCase[5]); //7.reduce 代表归约或者叫折叠,它是max/min/count的更为通用的函数,将流中的元素 //归约为一个值。有三个reduce函数: //Optional<T> reduce(BinaryOptional<T> accumulator), //T reduce(T identity,BinaryOptional<T> accumulator),identity表示初始值 //<U> U reduce(U identity,BinaryOptional<U,? super T,U> accumulator,BinaryOptional<U> combiner) //求分数最高的学生 StudentTestCase studentTestCase1 = testCases.stream() .reduce((accu,t) -> { if(accu.getScore() > t.getScore()){ return accu; }else{ return t; } }).get(); } }

总结:

复制代码
1
2
3
文章详细的介绍了lambda 的由来,作用以及用法。希望看本博文的读者能有所收获。如果你觉得文章写的不错,请点个赞,加个收藏哦!!!!

最后

以上就是深情飞机最近收集整理的关于Lambda表达式详细讲解---满满的干货哦Lambda 表达式的全部内容,更多相关Lambda表达式详细讲解---满满的干货哦Lambda内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(93)

评论列表共有 0 条评论

立即
投稿
返回
顶部