Main reference

[1] http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples

1. How Streams Work

A stream represents a sequence of elements and supports different kind of operations to perform computations upon those elements:

List<String> myList =
Arrays.asList("a1", "a2", "b1", "c2", "c1"); myList
.stream()
.filter(s -> s.startsWith("c"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println); // C1
// C2

2. Different Kinds of Streams

Streams can be created from various data sources, especially collections. Lists and Sets support new methods stream() and parallelStream() to either create a sequential or a parallel stream.

For Sequencial Stream

Arrays.asList("a1", "a2", "a3")
.stream()
.findFirst()
.ifPresent(System.out::println); // a1

Calling the method stream() on a list of objects returns a regular object stream. But we don't have to create collections in order to work with streams as we see in the next code sample:

Stream.of("a1", "a2", "a3")
.findFirst()
.ifPresent(System.out::println); // a1

Just use Stream.of() to create a stream from a bunch of object references.

Besides regular object streams Java 8 ships with special kinds of streams for working with the primitive data types intlong and double. As you might have guessed it's IntStream,LongStream and DoubleStream.

IntStreams can replace the regular for-loop utilizing IntStream.range()

IntStream.range(1, 4)
.forEach(System.out::println); // 1
// 2
// 3

All those primitive streams work just like regular object streams with the following differences: Primitive streams use specialized lambda expressions, e.g. IntFunction instead ofFunction or IntPredicate instead of Predicate. And primitive streams support the additional terminal aggregate operations sum() and average()

Arrays.stream(new int[] {1, 2, 3})
.map(n -> 2 * n + 1)
.average()
.ifPresent(System.out::println); // 5.0

Sometimes it's useful to transform a regular object stream to a primitive stream or vice versa. For that purpose object streams support the special mapping operations mapToInt(),mapToLong() and mapToDouble

Stream.of("a1", "a2", "a3")
.map(s -> s.substring(1))
.mapToInt(Integer::parseInt)
.max()
.ifPresent(System.out::println); // 3

Primitive streams can be transformed to object streams via mapToObj()

IntStream.range(1, 4)
.mapToObj(i -> "a" + i)
.forEach(System.out::println); // a1
// a2
// a3

Here's a combined example: the stream of doubles is first mapped to an int stream and than mapped to an object stream of strings:

Stream.of(1.0, 2.0, 3.0)
.mapToInt(Double::intValue)
.mapToObj(i -> "a" + i)
.forEach(System.out::println); // a1
// a2
// a3

3. Processing Order

An important characteristic of intermediate operations is laziness.

4. Reusing Streams

Java 8 streams cannot be reused. As soon as you call any terminal operation the stream is closed

Stream<String> stream =
Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> s.startsWith("a")); stream.anyMatch(s -> true); // ok
stream.noneMatch(s -> true); // exception

To overcome this limitation we have to to create a new stream chain for every terminal operation we want to execute, e.g. we could create a stream supplier to construct a new stream with all intermediate operations already set up:

Supplier<Stream<String>> streamSupplier =
() -> Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> s.startsWith("a")); streamSupplier.get().anyMatch(s -> true); // ok
streamSupplier.get().noneMatch(s -> true); // ok

Each call to get() constructs a new stream on which we are save to call the desired terminal operation.

5. Advanced Operations

Most code samples from this section use the following list of persons for demonstration purposes:

class Person {
String name;
int age; Person(String name, int age) {
this.name = name;
this.age = age;
} @Override
public String toString() {
return name;
}
} List<Person> persons =
Arrays.asList(
new Person("Max", 18),
new Person("Peter", 23),
new Person("Pamela", 23),
new Person("David", 12));

Collect

Collect is an extremely useful terminal operation to transform the elements of the stream into a different kind of result, e.g. a ListSet or Map. Collect accepts a Collector which consists of four different operations: a supplier, an accumulator, a combiner and a finisher. This sounds super complicated at first, but the good part is Java 8 supports various built-in collectors via the Collectors class. So for the most common operations you don't have to implement a collector yourself.

List<Person> filtered =
persons
.stream()
.filter(p -> p.name.startsWith("P"))
.collect(Collectors.toList()); System.out.println(filtered); // [Peter, Pamela]

Then

Map<Integer, List<Person>> personsByAge = persons
.stream()
.collect(Collectors.groupingBy(p -> p.age)); personsByAge
.forEach((age, p) -> System.out.format("age %s: %s\n", age, p)); // age 18: [Max]
// age 23: [Peter, Pamela]
// age 12: [David]

Collectors are extremely versatile. You can also create aggregations on the elements of the stream, e.g. determining the average age of all persons:

Double averageAge = persons
.stream()
.collect(Collectors.averagingInt(p -> p.age)); System.out.println(averageAge); // 19.0

In order to transform the stream elements into a map, we have to specify how both the keys and the values should be mapped. Keep in mind that the mapped keys must be unique, otherwise an IllegalStateException is thrown. You can optionally pass a merge function as an additional parameter to bypass the exception:

Map<Integer, String> map = persons
.stream()
.collect(Collectors.toMap(
p -> p.age,
p -> p.name,
(name1, name2) -> name1 + ";" + name2)); System.out.println(map);
// {18=Max, 23=Peter;Pamela, 12=David}

Now that we know some of the most powerful built-in collectors, let's try to build our own special collector. We want to transform all persons of the stream into a single string consisting of all names in upper letters separated by the | pipe character. In order to achieve this we create a new collector via Collector.of(). We have to pass the four ingredients of a collector: a supplier, an accumulator, a combiner and a finisher.

Collector<Person, StringJoiner, String> personNameCollector =
Collector.of(
() -> new StringJoiner(" | "), // supplier
(j, p) -> j.add(p.name.toUpperCase()), // accumulator
(j1, j2) -> j1.merge(j2), // combiner
StringJoiner::toString); // finisher String names = persons
.stream()
.collect(personNameCollector); System.out.println(names); // MAX | PETER | PAMELA | DAVID

Since strings in Java are immutable, we need a helper class like StringJoiner to let the collector construct our string. The supplier initially constructs such a StringJoiner with the appropriate delimiter. The accumulator is used to add each persons upper-cased name to the StringJoiner. The combiner knows how to merge two StringJoiners into one. In the last step the finisher constructs the desired String from the StringJoiner.

FlatMap

We've already learned how to transform the objects of a stream into another type of objects by utilizing the map operation. Map is kinda limited because every object can only be mapped to exactly one other object. But what if we want to transform one object into multiple others or none at all? This is where flatMap comes to the rescue.

FlatMap transforms each element of the stream into a stream of other objects. So each object will be transformed into zero, one or multiple other objects backed by streams. The contents of those streams will then be placed into the returned stream of the flatMap operation.

Before we see flatMap in action we need an appropriate type hierarchy:

class Foo {
String name;
List<Bar> bars = new ArrayList<>(); Foo(String name) {
this.name = name;
}
} class Bar {
String name; Bar(String name) {
this.name = name;
}
}

Next, we utilize our knowledge about streams to instantiate a couple of objects:

List<Foo> foos = new ArrayList<>();

// create foos
IntStream
.range(1, 4)
.forEach(i -> foos.add(new Foo("Foo" + i))); // create bars
foos.forEach(f ->
IntStream
.range(1, 4)
.forEach(i -> f.bars.add(new Bar("Bar" + i + " <- " + f.name))));

Now we have a list of three foos each consisting of three bars.

FlatMap accepts a function which has to return a stream of objects. So in order to resolve the bar objects of each foo, we just pass the appropriate function:

foos.stream()
.flatMap(f -> f.bars.stream())
.forEach(b -> System.out.println(b.name)); // Bar1 <- Foo1
// Bar2 <- Foo1
// Bar3 <- Foo1
// Bar1 <- Foo2
// Bar2 <- Foo2
// Bar3 <- Foo2
// Bar1 <- Foo3
// Bar2 <- Foo3
// Bar3 <- Foo3

Finally, the above code example can be simplified into a single pipeline of stream operations:

IntStream.range(1, 4)
.mapToObj(i -> new Foo("Foo" + i))
.peek(f -> IntStream.range(1, 4)
.mapToObj(i -> new Bar("Bar" + i + " <- " f.name))
.forEach(f.bars::add))
.flatMap(f -> f.bars.stream())
.forEach(b -> System.out.println(b.name));

FlatMap is also available for the Optional class introduced in Java 8. Optionals flatMapoperation returns an optional object of another type. So it can be utilized to prevent nastynull checks.

Think of a highly hierarchical structure like this:

class Outer {
Nested nested;
} class Nested {
Inner inner;
} class Inner {
String foo;
}

In order to resolve the inner string foo of an outer instance you have to add multiple null checks to prevent possible NullPointerExceptions:

Outer outer = new Outer();
if (outer != null && outer.nested != null && outer.nested.inner != null) {
System.out.println(outer.nested.inner.foo);
}

The same behavior can be obtained by utilizing optionals flatMap operation:

Optional.of(new Outer())
.flatMap(o -> Optional.ofNullable(o.nested))
.flatMap(n -> Optional.ofNullable(n.inner))
.flatMap(i -> Optional.ofNullable(i.foo))
.ifPresent(System.out::println);

Reduce

The reduction operation combines all elements of the stream into a single result. Java 8 supports three different kind of reduce methods. The first one reduces a stream of elements to exactly one element of the stream.

persons
.stream()
.reduce((p1, p2) -> p1.age > p2.age ? p1 : p2)
.ifPresent(System.out::println); // Pamela

The second reduce method accepts both an identity value and a BinaryOperatoraccumulator. This method can be utilized to construct a new Person with the aggregated names and ages from all other persons in the stream:

Person result =
persons
.stream()
.reduce(new Person("", 0), (p1, p2) -> {
p1.age += p2.age;
p1.name += p2.name;
return p1;
}); System.out.format("name=%s; age=%s", result.name, result.age);
// name=MaxPeterPamelaDavid; age=76

The third reduce method accepts three parameters: an identity value, a BiFunctionaccumulator and a combiner function of type BinaryOperator. Since the identity values type is not restricted to the Person type, we can utilize this reduction to determine the sum of ages from all persons:

Integer ageSum = persons
.stream()
.reduce(0, (sum, p) -> sum += p.age, (sum1, sum2) -> sum1 + sum2); System.out.println(ageSum); // 76

6. Parallel Streams

It can be stated that parallel streams can bring be a nice performance boost to streams with a large amount of input elements. But keep in mind that some parallel stream operations like reduce and collect need additional computations (combine operations) which isn't needed when executed sequentially.

Furthermore we've learned that all parallel stream operations share the same JVM-wide common ForkJoinPool. So you probably want to avoid implementing slow blocking stream operations since that could potentially slow down other parts of your application which rely heavily on parallel streams.

Java 8 Learn Notes - Streams的更多相关文章

  1. Java 8 Learn Notes

    Main reference: [1] http://winterbe.com/posts/2014/03/16/java-8-tutorial/ [2] https://plus.google.co ...

  2. Forget Java to learn Javascript from 0.--Day 1

    The first day,when I read 'we need practice so we need a Javascript Interpreter.','Every browser has ...

  3. Forget Java to learn Javascript from 0.--Preface

    I'm going to start to learn Javascript in this month. Someone told me you can't learn another langua ...

  4. Linux Academy Learn Notes

    Linux Essentials Certification Globbing ls ?.txt --- ? stands for one character while * means one or ...

  5. [Java coding] leetcode notes

    1, 如何不排序而找到最大,次大或者最小值? var int max1, max2, min1; iterate array once: update max1, max2, min1, for ex ...

  6. Java 8 中的 Streams API 详解

    为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 ...

  7. Bash Scripting Learn Notes

    References: [1] http://www.tldp.org/LDP/Bash-Beginners-Guide/html/ 1. Executing programs from a scri ...

  8. Java 8中的 Streams API 详解

    为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 ...

  9. (转)Java 8 中的 Streams API 详解

    为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 ...

随机推荐

  1. thinkphp3.2.x多图上传并且生成多张缩略图

    html部分 <!DOCTYPE html><html><head><meta http-equiv="Content-Type" con ...

  2. JS的作用域浅谈

    作为前端小白,总是对JS的作用域有点迷糊,这里稍微研究了一下分享出来,希望和我一样的小白可以学的一点 首先是一个经典的例子: var a=0,b=0; for (var i = 0; i < 1 ...

  3. WSDL/WebService/SOAP/REST/AXIS/CXF 开放式服务

    WebService是一种数据交换标准.通过WebService标准,你可以把项目中的方法作为接口提供给其他项目使用. 有时候我们习惯性地将具体提供服务的某个方法称为WebService.比如图书系统 ...

  4. Xamarin XAML语言教程使用Xamarin Studio创建XAML(二)

    Xamarin XAML语言教程使用Xamarin Studio创建XAML(二) 使用Xamarin Studio创建XAML Xamarin Studio和Visual Studio创建XAML文 ...

  5. Bug 笔记

    1.页面返回 400 Bag request: 原因:使用Spring  MVC  controller的时候,查询数据库:当数据库的数据类型是int型时,Spring MVC在查询的数据匹配给实体类 ...

  6. TCP基础知识 复习

    前言 说来惭愧,大二时候学的计算机网络好多都不太记得了,不过还好有认真学过,捡起来也挺快的,就是对于现在业界中使用的网络算法的不是很懂: 1 TCP报文段结构 1.1 序号和确认号 序号,是报文段首字 ...

  7. 【从无到有】JavaScript新手教程——2.分支结构和循环

    介绍完JS的简介和向量以及运算符,大家对JS也有了初步的了解和认识,今天带大家来看一下JS中常用的分支结构以及循环结构是怎么使用的 [JS中的分支结构] 一.[if-else结构] 1.结构写法: i ...

  8. java运算符 与(&)、非(~)、或(|)、异或(^)

    最近看HashMap源码,遇到了这样一段代码: static final int hash(Object key) { int h; return (key == null) ? 0 : (h = k ...

  9. IDEA第一章----下载安装idea,设置背景字体编码,配置JDK/Maven

    写在前面的话: 在程序的世界混迹了5年+,认真过,蹉跎过,回首突然发现自己得到的东西却很少.于是想写点东西记录下学习.工作抑或生活的种种,人生不只是眼前的苟且还有诗和远方,任沧海桑田韶华不在,愿无岁月 ...

  10. 统计学习方法:核函数(Kernel function)

    作者:桂. 时间:2017-04-26  12:17:42 链接:http://www.cnblogs.com/xingshansi/p/6767980.html 前言 之前分析的感知机.主成分分析( ...