1.1 从迭代到流操作

最近在阅读 Java 核心技术卷2高级特性时,由于是英文文档,就试着翻译了一下,其中部分单词还是需要借助 Google 翻译,希望在之后的阅读翻译过程中,会有很大的进步。

1.1 从迭代到流操作

当处理一个集合时,你通常通过迭代遍历集合的每一个元素并对其进行操作。例如,我们想统计一本书中的长单词,首先,我们要把它们放进 list 中

1
2
3
4
String contents = new String(Files.readAllBytes(
Paths.get("alice.txt")), StandardCharsets.UTF_8); // Read file into string
List<String> words = Arrays.asList(contents.split("[\\P{L}]+"));
// Split into words; nonletters are delimiters

然后我们开始迭代

1
2
3
4
5
int count = 0;
for (String w : words)
{
if (w.length() > 12) count++;
}

当用到 stream 时,同样的操作可能是这样的。

1
2
3
long count = words.stream()
.filter(w -> w.length() > 12)
.count();

现在,无需通过循环来获得过滤和计数的节点,方法名称即可告知你代码打算做什么。此外,在循环详细规定了操作顺序的情况下,只要结果正确,stream 就可以按其希望的任何方式调度操作。

只要把 stream 改为 parallelStream,就可以使 stream library 并行过滤以及计数

1
2
3
long count = words.parallelStream()
.filter(w -> w.length() > 12)
.count();

Streams 遵循“是什么,而不是怎么做”的原则,在最初的流示例中,我们描述了需要做到什么:获得长单词并对其计数。我们没有指明过滤的顺序以及线程。相反,本节开头的循环遍历指定了过滤的工作方式,从而放弃了任何优化此过滤的机会。

流从表面上看的话,与集合类似,允许你转换并操作数据,但是它们两者之间有根本性的区别。

  • 流不存储它的元素,它们可以存储在基础的集合中或者通过指令来生成
  • 流的操作不会改变其原来的数据源。例如,filter() 方法不会从新的流中移除元素,但是会生成一个不存在它们的新流。
  • 流的操作是懒加载的,就是说如果结果不需要的话,它们是不会继续执行的。例如,如果你只需要前五个长单词而不是全部,filter 方法将在匹配到第五个长单词后停止过滤。结果,你甚至可以有无限的流。

让我们来看一个其他的例子,stream() 与 parallelStream() 方法生成了包含单词列表的流,filter() 方法返回了一个新的包含那些长度大于12的长单词的流,count() 方法将该流减少为结果。

当你使用流时,可以通过以下三步操作来进行。

  • 创建一个 stream
  • 指定将初始的流转化为其他流的步骤
  • 应用终端操作来产生结果,此操作将强制执行之前的惰性操作。之后,将无法使用该流。

在 1.1 中的例子,用 stream() 或者 parallelStream() 方法来创建流,filter() 方法会处理这个流,count() 方法会产生最终结果。

以下是 1.1 中所用的部分代码

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
 1      package streams;
2
3 import java.io.IOException;
4 import java.nio.charset.StandardCharsets;
5 import java.nio.file.Files;
6 import java.nio.file.Paths;
7 import java.util.Arrays;
8 import java.util.List;
9
10 public class CountLongWords
11 {
12 public static void main(String[] args) throws IOException
13 {
14 String contents = new String(Files.readAllBytes(
15 StandardCharsets.UTF_8);
16 List<String> words = Arrays.asList(contents.split("\\PL+"));
17
18 long count = 0;
19 for (String w : words)
20 {
21 if (w.length() > 12) count++;
22 }
23 System.out.println(count);
24
25 count = words.stream().filter(w -> w.length() > 12).count();
26 System.out.println(count);
27
28 count = words.parallelStream().filter(w -> w.length() > 12).count();
29 System.out.println(count);
30 }
31 }

java.util.stream.Stream<T> 8
Stream<T> filter(Predicate<T> p)
Yields a stream containing all elements of this stream fulfilling p.
long count()
Yields the number of elements of this stream. This is a terminal operation.

java.util.Collection<E> 1.2
default Stream<E> stream()
default Stream<E> parallelStream()
Yields a sequential or parallel stream of the elements in this collection.
坚持原创技术分享,您的支持将鼓励我继续创作!