Java8 lambda表达式存在的意义及使用示例

本贴最后更新于 1931 天前,其中的信息可能已经水流花落
Lambda表达式有这么重要的两点
1. Lambda表达式是一个很小且能被当作数据进行传递的函数。
2. 集合对象在内部的遍历方式,这种遍历不同于当前已有的外部顺序化遍历。


###1. 内部循环和外部循环的区别
先举个例子
```
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);  
for (int number : numbers) {  
    System.out.println(number);  
} 
```
这就是外部循环的写法,这样写有什么缺点呢,有这么几个:
>* 只能顺序处理List中的元素(process one by one)
>* 不能充分利用多核CPU
>* 不利于编译器优化
而如果用内部循环,写成这样:
```
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);  
numbers.forEach((Integer value) -> System.out.println(value)); 
```

这种内部循环的写法,能带来这么几个好处:
>* 不一定需要顺序处理List中的元素,顺序可以不确定
>* 可以并行处理,充分利用多核CPU的优势
>* 有利于JIT编译器对代码进行优化

###2. 为什么你需要Lambda表达式
有了上面的对比,很清晰的就能知道为什么需要lambda表达式了。
很多时候,我们希望集合能够执行如下一种或几种操作:
1. 创建一个新的集合对象,但要过滤掉不符合条件的元素。
2. 对集合中的元素逐一进行转化,并使用转化后的集合。
3. 创建集合中所有元素的某个属性的总体值,例如,合计值与平均值。这样的任务(分别称之为过滤,映射和化简)具有共通的要点:它们都需要处理集合中的每个元素。

程序无论是判定某个元素是否存在,或是判断元素是否符合某个条件(过滤),或是将元素转化成新元素并生成新集合(映射),或是计算总体值(化简),关键原理就是"程序必须处理到集合中的每个元素"。
这就暗示我们需要一种简单的途径去表示用于内部遍历的程序。在Groovy中,就有提供这么完备的集合操作,他是通过闭包的方式来写:举个例子
collect方法–对集合每个元素进行运算后,得到一个新集合
```
def list = [1, 2, 3, 4]
def result = list.collect{it*2}
```
过滤–筛选出符合条件的元素,生成一个新集合
```
def list = [1, 2, 3, 4]
def result = list.grep{ it > 2 }
println result
```
看到这样书写给开发带来了很大的便利。所以java 8也提供了lambda的实现

那lambda的优点,总结出来就有这么几点:
####2.1 更紧凑的代码
Lambda表达式以一种简洁的方式去实现仅有一个方法的Java类。
如果代码中有大量的匿名内部类--诸如用于UI应用中的监听器与处理器实现,以及用于并发应用中的Callable与Runnable实现--在使用了Lambda表达式之后,将使代码变得非常短,且更易于理解。

####2.2. 通过提供额外的功能对方法的功能进行修改的能力
有时,方法不具备我们想要的一些功能。例如,Collection接口中的contains()方法只有当传入的对象确实存在于该集合对象中时才会返回true。但我们无法去干预该方法的功能,比如,若使用不同的大小写方案也可以认为正在查找的字符串存在于这个集合对象中,我们希望此时contains()方法也能返回true。

简单点儿说,我们所期望做的就是"将我们自己的新代码传入"已有的方法中,然后再调用这个传进去的代码。Lambda表达式提供了一种很好的途径来代表这种被传入已有方法且应该还会被回调的代码。
####2.3. 更好地支持多核处理
当今的CPU具备多个内核。这就意味着,多线程程序能够真正地被并行执行,这完全不同于在单核CPU中使用时间共享这种方式。通过在Java中支持函数式编程语法,Lambda表达式能帮助你编写简单的代码去高效地应用这些CPU内核。

例如,你能够并行地操控大集合对象,通过利用并行编程模式,如过滤、映射和化简(后面将会很快接触到这些模式),就可使用到CPU中所有可用的硬件线程。

###3. Lambda表达式的语法规则
Lambda表达式的基本格式是以一个可被接受的`参数列表`开头,以一些代码(称之为`表达式体/body`)结尾,并以`箭头(->)`将前两者分隔开。
例如:
```
Arrays.sort(dogArray, (Dog m, Dog n) -> m.getWeight() - n.getWeight());
printDogs(dogArray);
```

###4. Stream
Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。

而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 `Fork/Join` 框架(JSR166y)来拆分任务和加速处理过程。

**流的构成**
当我们使用一个流的时候,通常包括三个基本步骤:
获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道

使用举例
1. 挑出偶数
```
Integer[] sixNums = {1, 2, 3, 4, 5, 6};
Integer[] evens = Stream.of(sixNums).filter(n -> n%2 == 0).toArray(Integer[]::new);
```

2.把单词挑出来
```
List<String> output = reader.lines().
flatMap(line -> Stream.of(line.split(REGEXP))).
filter(word -> word.length() > 0).
collect(Collectors.toList());
```
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    2991 引用 • 8145 回帖 • 590 关注

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...