Introduction to Java 8 Streams – Java 8 Streams简介

最后修改: 2016年 5月 31日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll have a quick look at one of the major pieces of new functionality Java 8 had added – Streams.

在这篇文章中,我们将快速浏览一下Java 8增加的主要新功能之一–流。

We’ll explain what streams are about and showcase the creation and basic stream operations with simple examples.

我们将解释流是怎么回事,并通过简单的例子展示流的创建和基本操作。

2. Stream API

2.流媒体API

One of the major new features in Java 8 is the introduction of the stream functionality – java.util.stream – which contains classes for processing sequences of elements.

Java 8的主要新特性之一是引入了流功能–java.util.stream–它包含了用于处理元素序列的类。

The central API class is the Stream<T>. The following section will demonstrate how streams can be created using the existing data-provider sources.

中心 API 类是 Stream<T>下面的部分将演示如何使用现有的数据提供源来创建流。

2.1. Stream Creation

2.1.流的创建

Streams can be created from different element sources e.g. collection or array with the help of stream() and of() methods:

stream()of()方法的帮助下,可以从不同的元素源创建流,例如集合或数组。

String[] arr = new String[]{"a", "b", "c"};
Stream<String> stream = Arrays.stream(arr);
stream = Stream.of("a", "b", "c");

A stream() default method is added to the Collection interface and allows creating a Stream<T> using any collection as an element source:

一个stream()默认方法被添加到Collection接口中,并允许创建一个Stream<T> 使用任何集合作为一个元素源

Stream<String> stream = list.stream();

2.2. Multi-threading With Streams

2.2.使用流的多线程

Stream API also simplifies multithreading by providing the parallelStream() method that runs operations over stream’s elements in parallel mode.

流API还通过提供parallelStream()方法简化了多线程,该方法以并行模式在流的元素上运行操作。

The code below allows to run method doWork() in parallel for every element of the stream:

下面的代码允许对流中的每个元素并行地运行方法doWork()

list.parallelStream().forEach(element -> doWork(element));

In the following section, we will introduce some of the basic Stream API operations.

在下一节中,我们将介绍一些基本的Stream API操作。

3. Stream Operations

3.流水作业

There are many useful operations that can be performed on a stream.

有许多有用的操作可以在一个流上进行。

They are divided into intermediate operations (return Stream<T>) and terminal operations (return a result of definite type). Intermediate operations allow chaining.

它们分为中间操作(返回Stream<T>)和终端操作(返回一个确定类型的结果)。中间操作允许连锁操作。

It’s also worth noting that operations on streams don’t change the source.

还值得注意的是,对流的操作并不改变源。

Here’s a quick example:

这里有一个简单的例子。

long count = list.stream().distinct().count();

So, the distinct() method represents an intermediate operation, which creates a new stream of unique elements of the previous stream. And the count() method is a terminal operation, which returns stream’s size.

所以,distinct()方法代表了一个中间操作,它创建了一个由前一个流的唯一元素组成的新流。而count()方法是一个终端操作它返回流的大小。

3.1. Iterating

3.1.迭代

Stream API helps to substitute for, for-each, and while loops. It allows concentrating on operation’s logic, but not on the iteration over the sequence of elements. For example:

流API有助于替代forfor-eachwhile循环。它允许集中在操作的逻辑上,而不是在元素序列的迭代上。比如说。

for (String string : list) {
    if (string.contains("a")) {
        return true;
    }
}

This code can be changed just with one line of Java 8 code:

这段代码只需用一行Java 8的代码就可以改变。

boolean isExist = list.stream().anyMatch(element -> element.contains("a"));

3.2. Filtering

3.2.过滤

The filter() method allows us to pick a stream of elements that satisfy a predicate.

filter()方法允许我们挑选一个满足预测条件的元素流。

For example, consider the following list:

例如,考虑下面的清单。

ArrayList<String> list = new ArrayList<>();
list.add("One");
list.add("OneAndOnly");
list.add("Derek");
list.add("Change");
list.add("factory");
list.add("justBefore");
list.add("Italy");
list.add("Italy");
list.add("Thursday");
list.add("");
list.add("");

The following code creates a Stream<String> of the List<String>, finds all elements of this stream which contain char “d”, and creates a new stream containing only the filtered elements:

下面的代码创建了一个List<String>Stream<String>,找到这个流中所有包含char “d”的元素,并创建一个只包含被过滤元素的新流。

Stream<String> stream = list.stream().filter(element -> element.contains("d"));

3.3. Mapping

3.3.绘图

To convert elements of a Stream by applying a special function to them and to collect these new elements into a Stream, we can use the map() method:

为了通过对Stream的元素应用一个特殊的函数进行转换,并将这些新元素收集到Stream,我们可以使用map()方法。

List<String> uris = new ArrayList<>();
uris.add("C:\\My.txt");
Stream<Path> stream = uris.stream().map(uri -> Paths.get(uri));

So, the code above converts Stream<String> to the Stream<Path> by applying a specific lambda expression to every element of the initial Stream.

因此,上面的代码将Stream<String>转换为Stream<Path>,对初始Stream的每个元素应用一个特定的lambda表达式。

If you have a stream where every element contains its own sequence of elements and you want to create a stream of these inner elements, you should use the flatMap() method:

如果你有一个流,其中每个元素都包含自己的元素序列,你想创建一个这些内部元素的流,你应该使用flatMap()方法。

List<Detail> details = new ArrayList<>();
details.add(new Detail());
Stream<String> stream
  = details.stream().flatMap(detail -> detail.getParts().stream());

In this example, we have a list of elements of type Detail. The Detail class contains a field PARTS, which is a List<String>. With the help of the flatMap() method, every element from field PARTS will be extracted and added to the new resulting stream. After that, the initial Stream<Detail> will be lost.

在这个例子中,我们有一个类型为Detail的元素列表。Detail类包含一个字段PARTS,它是一个List<String>。在flatMap()方法的帮助下,字段PARTS中的每个元素将被提取并添加到新的结果流中。之后,最初的Stream<Detail>将丢失.

3.4. Matching

3.4.匹配

Stream API gives a handy set of instruments to validate elements of a sequence according to some predicate. To do this, one of the following methods can be used: anyMatch(), allMatch(), noneMatch(). Their names are self-explanatory. Those are terminal operations that return a boolean:

流API提供了一套方便的工具,可以根据一些谓词验证序列的元素。要做到这一点,可以使用以下方法之一。anyMatch(), allMatch(), noneMatch()。它们的名字是不言自明的。这些是返回boolean的终端操作。

boolean isValid = list.stream().anyMatch(element -> element.contains("h")); // true
boolean isValidOne = list.stream().allMatch(element -> element.contains("h")); // false
boolean isValidTwo = list.stream().noneMatch(element -> element.contains("h")); // false

For empty streams, the allMatch() method with any given predicate will return true:

对于空流,带有任何指定谓词的allMatch()方法将返回true

Stream.empty().allMatch(Objects::nonNull); // true

This is a sensible default, as we can’t find any element that doesn’t satisfy the predicate.

这是一个明智的默认,因为我们找不到任何不满足谓词的元素。

Similarly, the anyMatch() method always returns false for empty streams:

同样地,anyMatch()方法对于空流总是返回false

Stream.empty().anyMatch(Objects::nonNull); // false

Again, this is reasonable, as we can’t find an element satisfying this condition.

同样,这也是合理的,因为我们找不到一个满足这个条件的元素。

3.5. Reduction

3.5.削减

Stream API allows reducing a sequence of elements to some value according to a specified function with the help of the reduce() method of the type Stream. This method takes two parameters: first – start value, second – an accumulator function.

Stream API允许在Stream类型的reduce()方法的帮助下,根据一个指定的函数将一个元素序列减少到某个值。这个方法需要两个参数:第一 – 起始值,第二 – 一个累加器函数。

Imagine that you have a List<Integer> and you want to have a sum of all these elements and some initial Integer (in this example 23). So, you can run the following code and result will be 26 (23 + 1 + 1 + 1).

想象一下,你有一个List<Integer>,你想得到所有这些元素和一些初始Integer(在这个例子中是23)的总和。因此,你可以运行以下代码,结果将是26(23+1+1+1)。

List<Integer> integers = Arrays.asList(1, 1, 1);
Integer reduced = integers.stream().reduce(23, (a, b) -> a + b);

3.6. Collecting

3.6.采集

The reduction can also be provided by the collect() method of type Stream. This operation is very handy in case of converting a stream to a Collection or a Map and representing a stream in the form of a single string. There is a utility class Collectors which provide a solution for almost all typical collecting operations. For some, not trivial tasks, a custom Collector can be created.

减少也可以由Stream类型的collect()方法提供。在将流转换为CollectionMap并以单个字符串的形式表示流时,这个操作非常方便。有一个实用类Collectors,为几乎所有典型的收集操作提供了解决方案。对于一些并非微不足道的任务,可以创建一个自定义的Collector

List<String> resultList 
  = list.stream().map(element -> element.toUpperCase()).collect(Collectors.toList());

This code uses the terminal collect() operation to reduce a Stream<String> to the List<String>.

这段代码使用终端collect()操作,将一个Stream<String>减少到List<String>.

4. Conclusions

4.结论

In this article, we briefly touched upon Java streams — definitely one of the most interesting Java 8 features.

在这篇文章中,我们简要地谈到了Java流–绝对是最有趣的Java 8功能之一。

There are many more advanced examples of using Streams; the goal of this write-up was only to provide a quick and practical introduction to what you can start doing with the functionality and as a starting point for exploring and further learning.

还有很多使用Streams的高级例子;这篇文章的目的只是提供一个快速实用的介绍,说明你可以用这个功能开始做什么,并作为一个探索和进一步学习的起点。

The source code accompanying the article is available over on GitHub.

文章所附的源代码可在GitHub上找到。