系列文章目录
前言
本文为java8函数式编程读书笔记。提示:以下是本篇文章正文内容,下面案例可供参考
一、使用迭代
1.外部迭代
先看一个示例:如果要计算从伦敦来的艺术家的人数,要使用外部迭代方式,通常我们会这么写
int count = 0;
for (Artist artist : allArtists) {
if (artist.isFrom("London"))
{
count++;
}
}
如上代码,for 循环其实是一个封装了迭代的语法糖,我们在这里多花点时间, 看看它的工作原理。首先调用 iterator 方法,产生一个新的 Iterator 对象,进而控制整 个迭代过程,这就是外部迭代。迭代过程通过显式调用 Iterator 对象的 hasNext 和 next 方法完成迭代,展开后的代码是这个样子滴
int count = 0;
Iterator<Artist> iterator = allArtists.iterator();
while(iterator.hasNext()) {
Artist artist = iterator.next();
if (artist.isFrom("London")) {
count++;
}
}
2.内部迭代
如下代码:
首先要注意 stream() 方法的调用,它和上面调用 iterator() 的作用一样。该方法不是返回一个控制迭代的 Iterator 对象,而是返 回内部迭代中的相应接口:Stream。
long count = allArtists.stream() .filter(artist -> artist.isFrom("London")) .count();
使用后的方法调用流程
具体来讲其大致可分为两步简单的操作:
- 找出所有来自伦敦的艺术家;
- 计算他们的人数。
每种操作都对应 Stream 接口的一个方法。为了找出来自伦敦的艺术家,需要对 Stream 对 象进行过滤:filter。过滤在这里是指“只保留通过某项测试的对象”。测试由一个函数完 成,根据艺术家是否来自伦敦,该函数返回 true 或者 false。由于 Stream API 的函数式编 程风格,我们并没有改变集合的内容,而是描述出 Stream 里的内容。count() 方法计算给 定 Stream 里包含多少个对象。
二、实现机制
整个过程被分解为两种更简单的操作:过滤和计数,看似有化简为繁之嫌—— 外部迭代中只含一个 for 循环,两种操作是否意味着需要两次循环?事实上,类库设计精妙, 只需对艺术家列表迭代一次。
通常,在 Java 中调用一个方法,计算机会随即执行操作:比如,System.out.println (“Hello World”); 会在终端上输出一条信息。Stream 里的一些方法却略有不同,它们虽是 普通的 Java 方法,但返回的 Stream 对象却不是一个新集合,而是创建新集合的配方。
1.惰性求值方法
示例1:只过滤,不计数
allArtists.stream() .filter(artist -> artist.isFrom("London"));
这行代码并未做什么实际性的工作,filter 只刻画出了 Stream,但没有产生新的集合。像 filter 这样只描述 Stream,最终不产生新集合的方法叫作惰性求值方法;
如果在过滤器中加入一条 println 语句,来输出艺术家的名字,就能轻而易举地看出其中的不 同。下面代码加入了输出语句。运行这段代码,程序不会输出任何信息!示例2:由于使用了惰性求值,没有输出艺术家的名字
allArtists.stream()
.filter(artist -> {
System.out.println(artist.getName());
return artist.isFrom("London");
});
2.及早求值方法
示例:计算来自伦敦的艺术家人数
long count = allArtists.stream() .filter(artist -> artist.isFrom("London")) .count();
上述代码中 像count 方法这样 最终会从 Stream 产生值的方法叫作及早求值方法。
所以更改上一节的 示例2,末尾加上count方法艺术家的名 字就会被输出。
long count = allArtists.stream()
.filter(artist -> {
System.out.println(artist.getName());
return artist.isFrom("London"); })
.count();
3.如何判断方法类型
判断一个操作是惰性求值还是及早求值很简单:只需看它的返回值。如果返回值是 Stream, 那么是惰性求值;如果返回值是另一个值或为空,那么就是及早求值。使用这些操作的理 想方式就是形成一个惰性求值的链,最后用一个及早求值的操作返回想要的结果,这正是 它的合理之处。计数的示例也是这样运行的,但这只是最简单的情况:只含两步操作。 整个过程和建造者模式有共通之处。建造者模式使用一系列操作设置属性和配置,最后调 用一个 build 方法,这时,对象才被真正创建。