Javalambda表达式10个⽰例
⼀、⽤lambda表达式实现Runnable
我开始使⽤Java 8时,⾸先做的就是使⽤lambda表达式替换匿名类,⽽实现Runnable接⼝是匿名类的最好⽰例。看⼀下Java 8之前的runnable实现⽅法,需要4⾏代码,⽽使⽤lambda表达式只需要⼀⾏代码。我们在这⾥做了什么呢?那就是⽤() -> {}代码块替代了整个。
// Java 8之前:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Before Java8, too much code for too little to do");
}
}).start();
//Java 8⽅式:
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();
输出:
too much code, for too little to do
Lambda expression rocks !!
这个例⼦向我们展⽰了Java 8 lambda表达式的语法。你可以使⽤lambda写出如下代码:
(params) -> expression
(params) -> statement
(params) -> { statements }
例如,如果你的⽅法不对参数进⾏修改、重写,只是在控制台打印点东西的话,那么可以这样写:
() -> System.out.println("Hello Lambda Expressions");
如果你的⽅法接收两个参数,那么可以写成如下这样:
(int even, int odd) -> even + odd
顺便提⼀句,通常都会把lambda表达式内部变量的名字起得短⼀些。这样能使代码更简短,放在同⼀⾏。所以,在上述代码中,变量名选⽤a、b或者x、y会⽐even、odd要好。
⼆、使⽤Java 8 lambda表达式进⾏事件处理
如果你⽤过Swing API编程,你就会记得怎样写事件监听代码。这⼜是⼀个旧版本简单匿名类的经典⽤例,但现在可以不这样了。你可以⽤lambda表达式写出更好的事件监听代码,如下所⽰:
// Java 8之前:
JButton show = new JButton("Show");
show.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Event handling without lambda expression is boring");
}
});
// Java 8⽅式:
show.addActionListener((e) -> {
System.out.println("Light, Camera, Action !! Lambda expressions Rocks");
});
Java开发者经常使⽤匿名类的另⼀个地⽅是为 Collections.sort() 定制 。在Java 8中,你可以⽤更可读的lambda表达式换掉丑陋的匿名类。我把这个留做练习,应该不难,可以按照我在使⽤lambda表达式实现 和 ActionListener 的过程中的套路来做。
三、使⽤lambda表达式对列表进⾏迭代
如果你使过⼏年Java,你就知道针对集合类,最常见的操作就是进⾏迭代,并将业务逻辑应⽤于各个元素,例如处理订单、交易和事件的列表。由于Java是命令式语⾔,Java 8之前的所有循环代码都是顺序的,即可以对其元素进⾏并⾏化处理。如果你想做并⾏过滤,就需要⾃⼰写代码,这并不是那么容易。通过引⼊lambda表达式和默认⽅法,将做什么和怎么做的问题分开了,这意味着Java集合现在知道怎样做迭代,并可以在API层⾯对集合元素进⾏并⾏处理。下⾯的例⼦⾥,我将介绍如何在或不使⽤lambda表达式的情况下迭代列表。你可以看到列表现在有了⼀个 forEach() ⽅法,它可以迭代所有对象,并将你的lambda代码应⽤在其中。
// Java 8之前:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
System.out.println(feature);
}
// Java 8之后:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
features.forEach(n -> System.out.println(n));
// 使⽤Java 8的⽅法引⽤更⽅便,⽅法引⽤由::双冒号操作符标⽰,
// 看起来像C++的作⽤域解析运算符
features.forEach(System.out::println);
输出:
Lambdas
Default Method
Stream API
Date and Time API
的最后⼀个例⼦展⽰了如何在Java 8中使⽤⽅法引⽤(method reference)。你可以看到C++⾥⾯的双冒号、范围解析操作符现在在Java 8中⽤来表⽰⽅法引⽤。
四、使⽤lambda表达式和函数式接⼝Predicate
除了在语⾔层⾯⽀持函数式编程风格,Java 8也添加了⼀个包,叫做 java.util.function。它包含了很多类,⽤来⽀持Java的函数式编程。其中⼀个便是Predicate,使⽤ java.util.function.Predicate 函数式接⼝以及lambda表达式,可以向API⽅法添加逻辑,⽤更少的代码⽀持更多的动态⾏为。下⾯是Java 8 Predicate 的例⼦,展⽰了过滤集合数据的多种常⽤⽅法。Predicate接⼝⾮常适⽤于做过滤。
public static void main(args[]){
List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
System.out.println("Languages which starts with J :");
filter(languages, (str)->str.startsWith("J"));
System.out.println("Languages which ends with a ");
filter(languages, (str)-&dsWith("a"));
System.out.println("Print all languages :");
filter(languages, (str)->true);
System.out.println("Print no language : ");
filter(languages, (str)->false);
System.out.println("Print language whose length greater than 4:");
filter(languages, (str)->str.length() > 4);
}
public static void filter(List names, Predicate condition) {
for(String name: names) {
st(name)) {
System.out.println(name + " ");
}
}
}
输出:
Languages which starts with J :
Java
Languages which ends with a
Java
Scala
Print all languages :
Java
Scala
C++
Haskell
Lisp
Print no language :
Print language whose length greater than 4:
Scala
Haskell
// 更好的办法
public static void filter(List names, Predicate condition) {
names.stream().filter((name) -> (st(name))).forEach((name) -> {
lambda编程System.out.println(name + " ");
});
}
可以看到,Stream API的过滤⽅法也接受⼀个Predicate,这意味着可以将我们定制的 filter() ⽅法替换成写在⾥⾯的内联代码,这就是lambda表达式的魔⼒。另外,Predicate接⼝也允许进⾏多重条件的测试,下个例⼦将要讲到。
五、如何在lambda表达式中加⼊Predicate
上个例⼦说到,java.util.function.Predicate 允许将两个或更多的 Predicate 合成⼀个。它提供类似于逻辑操作符AND和OR的⽅法,名字叫做and()、or()和xor(),⽤于将传⼊ filter() ⽅法的条件合并起来。例如,要得到所有以J开始,长度为四个字母的语⾔,可以定义两个独⽴的 Predicate ⽰例分别表⽰每⼀个条件,然后⽤ Predicate.and() ⽅法将它们合并起来,如下所⽰:
// 甚⾄可以⽤and()、or()和xor()逻辑函数来合并Predicate,
// 例如要到所有以J开始,长度为四个字母的名字,你可以合并两个Predicate并传⼊
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
names.stream()
.filter(startsWithJ.and(fourLetterLong))
.forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));
类似地,也可以使⽤ or() 和 xor() ⽅法。本例着重介绍了如下要点:可按需要将 Predicate 作为单独条件然后将其合并起来使⽤。简⽽⾔之,你可以以传统Java命令⽅式使⽤ Predicate 接⼝,也可以充分利⽤lambda表达式达到事半功倍的效果。
六、Java 8中使⽤lambda表达式的Map和Reduce⽰例
本例介绍最⼴为⼈知的函数式编程概念map。它允许你将对象进⾏转换。例如在本例中,我们将 costBeforeTax 列表的每个元素转换成为税后的值。我们将 x -> x*x lambda表达式传到 map() ⽅法,后者将其应⽤到流中的每⼀个元素。然后⽤ forEach() 将列表元素打印出来。使⽤流API的收集器类,可以得到所有含税的开销。有 toList() 这样的⽅法将 map 或任何其他操作的结果合并起来。由于收集器在流上做终端操作,因此之后便不能重⽤流了。你甚⾄可以⽤流API的 reduce() ⽅法将所有数字合成⼀个,下⼀个例⼦将会讲到。
// 不使⽤lambda表达式为每个订单加上12%的税
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
System.out.println(price);
}
// 使⽤lambda表达式
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
costBeforeTax.stream().map((cost) -> cost + .12*cost).forEach(System.out::println);
输出:
112.0
224.0
336.0
448.0
560.0
112.0
224.0
336.0
448.0
560.0
例6.2、Java 8中使⽤lambda表达式的Map和Reduce⽰例
在上个例⼦中,可以看到map将集合类(例如列表)元素进⾏转换的。还有⼀个 reduce() 函数可以将所有值合并成⼀个。Map和Reduce 操作是函数式编程的核⼼操作,因为其功能,reduce ⼜被称为折叠操作。另外,reduce 并不是⼀个新的操作,你有可能已经在使⽤它。SQL中类似 sum()、avg() 或者 count() 的聚集函数,实际上就是 reduce 操作,因为它们接收多个值并返回⼀个值。流API定义的reduceh() 函数可以接受lambda表达式,并对所有值进⾏合并。IntStream这样的类有类似 average()、count()、sum() 的内建⽅法来做reduce 操作,也有mapToLong()、mapToDouble() ⽅法来做转换。这并不会限制你,你可以⽤内建⽅法,也可以⾃⼰定义。在这个Java 8的Map Reduce⽰例⾥,我们⾸先对所有价格应⽤ 12% 的VAT,然后⽤ reduce() ⽅法计算总和。
// 为每个订单加上12%的税
// ⽼⽅法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
total = total + price;
}
System.out.println("Total : " + total);
// 新⽅法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);
输出:
Total : 1680.0
Total : 1680.0
七、通过过滤创建⼀个String列表
过滤是Java开发者在⼤规模集合上的⼀个常⽤操作,⽽现在使⽤lambda表达式和流API过滤⼤规模数据集合是惊⼈的简单。流提供了⼀个filter() ⽅法,接受⼀个 Predicate 对象,即可以传⼊⼀个lambda表达式作为过滤逻辑。下⾯的例⼦是⽤lambda表达式过滤Java集合,将帮助理解。
// 创建⼀个字符串列表,每个字符串长度⼤于2
List<String> filtered = strList.stream().filter(x -> x.length()> 2).List());
System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);
输出:
Original List : [abc, , bcd, , defg, jk], filtered list : [abc, bcd, defg]
另外,关于 filter() ⽅法有个常见误解。在现实⽣活中,做过滤的时候,通常会丢弃部分,但使⽤filter()⽅法则是获得⼀个新的列表,且其每个元素符合过滤原则。
⼋、对列表的每个元素应⽤函数
我们通常需要对列表的每个元素使⽤某个函数,例如逐⼀乘以某个数、除以某个数或者做其它操作。这些操作都很适合⽤ map() ⽅法,可以将转换逻辑以lambda表达式的形式放在 map() ⽅法⾥,就可以对集合的各个元素进⾏转换了,如下所⽰。
// 将字符串换成⼤写并⽤逗号链接起来
List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
System.out.println(G7Countries);
输出:
USA, JAPAN, FRANCE, GERMANY, ITALY, U.K., CANADA
九、复制不同的值,创建⼀个⼦列表
本例展⽰了如何利⽤流的 distinct() ⽅法来对集合进⾏去重。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论