Java8 lambda表达式10个示例<转>

例一、用lambda表达式实现Runnable

我开始使用Java 8时,首先作的就是使用lambda表达式替换匿名类,而实现Runnable接口是匿名类的最好示例。看一下Java 8以前的runnable实现方法,须要4行代码,而使用lambda表达式只须要一行代码。咱们在这里作了什么呢?那就是用() -> {}代码块替代了整个匿名类html

复制代码
// 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();
复制代码

输出:java

too much code, for too little to do
Lambda expression rocks !!

这个例子向咱们展现了Java 8 lambda表达式的语法。你可使用lambda写出以下代码:express

(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() 定制 Comparator。在Java 8中,你能够用更可读的lambda表达式换掉丑陋的匿名类。我把这个留作练习,应该不难,能够按照我在使用lambda表达式实现 Runnable 和 ActionListener 的过程当中的套路来作。jsp

例三、使用lambda表达式对列表进行迭代

若是你使过几年Java,你就知道针对集合类,最多见的操做就是进行迭代,并将业务逻辑应用于各个元素,例如处理订单、交易和事件的列表。因为Java是命令式语言,Java 8以前的全部循环代码都是顺序的,便可以对其元素进行并行化处理。若是你想作并行过滤,就须要本身写代码,这并非那么容易。经过引入lambda表达式和默认方法,将作什么和怎么作的问题分开了,这意味着Java集合如今知道怎样作迭代,并能够在API层面对集合元素进行并行处理。下面的例子里,我将介绍如何在使用lambda或不使用lambda表达式的状况下迭代列表。你能够看到列表如今有了一个 forEach()  方法,它能够迭代全部对象,并将你的lambda代码应用在其中。ide

复制代码
// 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)->str.endsWith("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)  {
        if(condition.test(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) -> (condition.test(name))).forEach((name) -> {
        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.二、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).collect(Collectors.toList());
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() 方法来对集合进行去重。

// 用全部不一样的数字建立一个正方形列表
List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
List<Integer> distinct = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
System.out.printf("Original List : %s,  Square Without duplicates : %s %n", numbers, distinct);

输出:

Original List : [9, 10, 3, 4, 7, 3, 4],  Square Without duplicates : [81, 100, 9, 16, 49]

例十、计算集合元素的最大值、最小值、总和以及平均值

IntStream、LongStream 和 DoubleStream 等流的类中,有个很是有用的方法叫作 summaryStatistics() 。能够返回 IntSummaryStatistics、LongSummaryStatistics 或者 DoubleSummaryStatistic s,描述流中元素的各类摘要数据。在本例中,咱们用这个方法来计算列表的最大值和最小值。它也有 getSum() 和 getAverage() 方法来得到列表的全部元素的总和及平均值。

复制代码
//获取数字的个数、最小值、最大值、总和以及平均值
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());
复制代码

输出:

Highest prime number in List : 29
Lowest prime number in List : 2
Sum of all prime numbers : 129
Average of all prime numbers : 12.9

Lambda表达式 vs 匿名类

既然lambda表达式即将正式取代Java代码中的匿名内部类,那么有必要对两者作一个比较分析。一个关键的不一样点就是关键字 this。匿名类的 this 关键字指向匿名类,而lambda表达式的 this 关键字指向包围lambda表达式的类。另外一个不一样点是两者的编译方式。Java编译器将lambda表达式编译成类的私有方法。使用了Java 7的 invokedynamic 字节码指令来动态绑定这个方法。

Java 8 Lambda表达式要点

10个Java lambda表达式、流API示例

到目前为止咱们看到了Java 8的10个lambda表达式,这对于新手来讲是个合适的任务量,你可能须要亲自运行示例程序以便掌握。试着修改要求建立本身的例子,达到快速学习的目的。我还想建议你们使用Netbeans IDE来练习lambda表达式,它对Java 8支持良好。当把代码转换成函数式的时候,Netbeans会及时给你提示。只需跟着Netbeans的提示,就能很容易地把匿名类转换成lambda表达式。此外,若是你喜欢阅读,那么记得看一下Java 8的lambdas,实用函数式编程这本书(Java 8 Lambdas, pragmatic functional programming),做者是Richard Warburton,或者也能够看看Manning的Java 8实战(Java 8 in Action),这本书虽然还没出版,但我猜线上有第一章的免费pdf。不过,在你开始忙其它事情以前,先回顾一下Java 8的lambda表达式、默认方法和函数式接口的重点知识。

1)lambda表达式仅能放入以下代码:预约义使用了 @Functional 注释的函数式接口,自带一个抽象函数的方法,或者SAM(Single Abstract Method 单个抽象方法)类型。这些称为lambda表达式的目标类型,能够用做返回类型,或lambda目标代码的参数。例如,若一个方法接收Runnable、Comparable或者 Callable 接口,都有单个抽象方法,能够传入lambda表达式。相似的,若是一个方法接受声明于 java.util.function 包内的接口,例如 Predicate、Function、Consumer 或 Supplier,那么能够向其传lambda表达式。

2)lambda表达式内可使用方法引用,仅当该方法不修改lambda表达式提供的参数。本例中的lambda表达式能够换为方法引用,由于这仅是一个参数相同的简单方法调用。

list.forEach(n -> System.out.println(n)); 
list.forEach(System.out::println);  // 使用方法引用

然而,若对参数有任何修改,则不能使用方法引用,而需键入完整地lambda表达式,以下所示:

list.forEach((String s) -> System.out.println("*" + s + "*"));

事实上,能够省略这里的lambda参数的类型声明,编译器能够从列表的类属性推测出来。

3)lambda内部可使用静态、非静态和局部变量,这称为lambda内的变量捕获。

4)Lambda表达式在Java中又称为闭包或匿名函数,因此若是有同事把它叫闭包的时候,不用惊讶。

5)Lambda方法在编译器内部被翻译成私有方法,并派发 invokedynamic 字节码指令来进行调用。可使用JDK中的 javap 工具来反编译class文件。使用 javap -p 或 javap -c -v 命令来看一看lambda表达式生成的字节码。大体应该长这样:

private static java.lang.Object lambda$0(java.lang.String);

6)lambda表达式有个限制,那就是只能引用 final 或 final 局部变量,这就是说不能在lambda内部修改定义在域外的变量。

List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { factor++; });
Compile time error : "local variables referenced from a lambda expression must be final or effectively final"

另外,只是访问它而不做修改是能够的,以下所示:

List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { System.out.println(factor*element); });

输出:

4
6
10
14

所以,它看起来更像不可变闭包,相似于Python。

以上就是Java 8的lambda表达式的所有10个例子。这次修改将成为Java史上最大的一次,将深远影响将来Java开发者使用集合框架的方式。我想规模最类似的一次修改就是Java 5的发布了,它带来了不少优势,提高了代码质量,例如:泛型、枚举、自动装箱(Autoboxing)、静态导入、并发API和变量参数。上述特性使得Java代码更加清晰,我想lambda表达式也将进一步改进它。我在期待着开发并行第三方库,这可使高性能应用变得更容易写。

转自 http://www.javashuo.com/article/p-gybkcbcp-eh.html

相关文章
相关标签/搜索