mirror of
https://github.com/LingCoder/OnJava8.git
synced 2026-08-24 15:03:29 +08:00
# 第十四章 流式编程 ## 流创建部分内容
# 第十四章 流式编程 ## 流创建部分
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
# 第十四章 流式编程
|
||||
|
||||
<!-- Java 8 Stream Support -->
|
||||
|
||||
## 流支持
|
||||
|
||||
Java 设计者面临着一个难题。他们拥有着一系列已经存在的库,这些库不仅仅在 Java 库本身中所使用,并且被用户编写的数百万行代码中使用。那他们如何将一个全新的流的基本概念融入到已经存在的库中?
|
||||
@@ -19,8 +20,239 @@ Java 8 中引入的解决方案是在[接口](10-Interfaces.md)中添加 **defau
|
||||
<!-- Stream Creation -->
|
||||
## 流创建
|
||||
|
||||
你可以通过 **Stream.of()** 很容易的将一组元素转发成为流(**Bubble** 类在之前的章节中已经定义过了):
|
||||
|
||||
```java
|
||||
// streams/StreamOf.java
|
||||
import java.util.stream.*;
|
||||
public class StreamOf {
|
||||
public static void main(String[] args) {
|
||||
Stream.of(new Bubble(1), new Bubble(2), new Bubble(3))
|
||||
.forEach(System.out::println);
|
||||
Stream.of("It's ", "a ", "wonderful ", "day ", "for ", "pie!")
|
||||
.forEach(System.out::print);
|
||||
System.out.println();
|
||||
Stream.of(3.14159, 2.718, 1.618)
|
||||
.forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
输出为:
|
||||
|
||||
```java
|
||||
Bubble(1)
|
||||
Bubble(2)
|
||||
Bubble(3)
|
||||
It's a wonderful day for pie!
|
||||
3.14159
|
||||
2.718
|
||||
1.618
|
||||
```
|
||||
|
||||
除此之外,每个 **Collection** 都可以通过 **stream()** 方法来产生一个流:
|
||||
|
||||
```java
|
||||
// streams/CollectionToStream.java
|
||||
import java.util.*;
|
||||
import java.util.stream.*;
|
||||
public class CollectionToStream {
|
||||
public static void main(String[] args) {
|
||||
List<Bubble> bubbles = Arrays.asList(new Bubble(1), new Bubble(2), new Bubble(3));
|
||||
System.out.println(bubbles.stream()
|
||||
.mapToint(b -> b.i)
|
||||
.sum());
|
||||
|
||||
Set<String> w = new HashSet<>(Arrays.asList("It's a wonderful day for pie!".split(" ")));
|
||||
w.stream()
|
||||
.map(x -> x + " ")
|
||||
.forEach(System.out::print);
|
||||
System.out.println();
|
||||
|
||||
Map<String, double> m = new HashMap<>();
|
||||
m.put("pi", 3.14159);
|
||||
m.put("e", 2.718);
|
||||
m.put("phi", 1.618);
|
||||
m.entrySet().stream()
|
||||
.map(e -> e.getKey() + ": " + e.getValue())
|
||||
.forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
输出为:
|
||||
|
||||
```java
|
||||
6
|
||||
a pie! It's for wonderful day
|
||||
phi: 1.618
|
||||
e: 2.718
|
||||
pi: 3.14159
|
||||
```
|
||||
|
||||
在创建 **List\<Bubble\>** 对象之后,我们只需要简单的调用所有集合中都有的方法 **stream()**。中间操作 **map()** 会获取流中的所有元素,并且对流中元素应用操作从而产生新的元素,并将其传递到流中。通常情况 **map()** 方法获取对象并产生新的对象,但是这里有特殊版本的方法用于数值类型的流。例如,**mapToInt()** 方法将一个对象流(objects stream)转换成为包含整形数字的 **IntStream**。同样有针对 **Float** 和 **Double** 的类似名字的操作。
|
||||
|
||||
我们通过在 **String** 类型上面应用 **split()** - split 方法会根据参数来拆分字符串 - 获取元素用于定义 **w**。稍后你会看到这个参数十分复杂,但是在这里我们只是根据空格来分割字符串。
|
||||
|
||||
为了从 **Map** 集合中产生流数据,我们首先调用 **entrySet()** 去产生一个对象流,每个对象都包含一个键以及与其相关联的值。然后调用 **getKey()** 和 **getValue()** 将其分开。
|
||||
|
||||
### 随机数流
|
||||
|
||||
**Random** 类被一组生成流的方法增强了:
|
||||
|
||||
```java
|
||||
// streams/RandomGenerators.java
|
||||
import java.util.*;
|
||||
import java.util.stream.*;
|
||||
public class RandomGenerators {
|
||||
public static <T> void show(Stream<T> stream) {
|
||||
stream
|
||||
.limit(4)
|
||||
.forEach(System.out::println);
|
||||
System.out.println("++++++++");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Random rand = new Random(47);
|
||||
show(rand.ints().boxed());
|
||||
show(rand.longs().boxed());
|
||||
show(rand.doubles().boxed());
|
||||
// Control the lower and upper bounds:
|
||||
show(rand.ints(10, 20).boxed());
|
||||
show(rand.longs(50, 100).boxed());
|
||||
show(rand.doubles(20, 30).boxed());
|
||||
// Control the stream size:
|
||||
show(rand.ints(2).boxed());
|
||||
show(rand.longs(2).boxed());
|
||||
show(rand.doubles(2).boxed());
|
||||
// Control the stream size and bounds:
|
||||
show(rand.ints(3, 3, 9).boxed());
|
||||
show(rand.longs(3, 12, 22).boxed());
|
||||
show(rand.doubles(3, 11.5, 12.3).boxed());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
输出为:
|
||||
|
||||
```java
|
||||
-1172028779
|
||||
1717241110
|
||||
-2014573909
|
||||
229403722
|
||||
++++++++
|
||||
2955289354441303771
|
||||
3476817843704654257
|
||||
-8917117694134521474
|
||||
4941259272818818752
|
||||
++++++++
|
||||
0.2613610344283964
|
||||
0.0508673570556899
|
||||
0.8037155449603999
|
||||
0.7620665811558285
|
||||
++++++++
|
||||
16
|
||||
10
|
||||
11
|
||||
12
|
||||
++++++++
|
||||
65
|
||||
99
|
||||
54
|
||||
58
|
||||
++++++++
|
||||
29.86777681078574
|
||||
24.83968447804611
|
||||
20.09247112332014
|
||||
24.046793846338723
|
||||
++++++++
|
||||
1169976606
|
||||
1947946283
|
||||
++++++++
|
||||
2970202997824602425
|
||||
-2325326920272830366
|
||||
++++++++
|
||||
0.7024254510631527
|
||||
0.6648552384607359
|
||||
++++++++
|
||||
6
|
||||
7
|
||||
7
|
||||
++++++++
|
||||
17
|
||||
12
|
||||
20
|
||||
++++++++
|
||||
12.27872414236691
|
||||
11.732085449736195
|
||||
12.196509449817267
|
||||
++++++++
|
||||
```
|
||||
|
||||
为了消除冗余代码,我创建了一个泛型方法 **show(Stream\<T\> stream)** (在讲解泛型之前就使用这个特性,确实有点作弊,但是回报是值得的)。类型参数 **T** 可以是任何类型,所以这个方法对 **Integer**, **Long** 和 **Double** 类型都生效。但是 **Random** 类只能生成原始数据类型 **int**, **long**, **double** 的流。幸运的是, **boxed()** 流操作将会自动的把基本类型包装成为对应的装箱类型,从而使得 **show()** 能够接受流。
|
||||
|
||||
我们可以使用 **Random** 为任意对象集合创建 **Supplier**。如下是一个从文本文件提供 **String** 对象的例子:
|
||||
|
||||
```java
|
||||
// streams/Cheese.dat
|
||||
Not much of a cheese shop really, is it?
|
||||
Finest in the district, sir.
|
||||
And what leads you to that conclusion?
|
||||
Well, it's so clean.
|
||||
It's certainly uncontaminated by cheese.
|
||||
We use the Files class to read all the lines from a file into a
|
||||
List<String> :
|
||||
```
|
||||
|
||||
```java
|
||||
// streams/RandomWords.java
|
||||
import java.util.*;
|
||||
import java.util.stream.*;
|
||||
import java.util.function.*;
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
public class RandomWords implements Supplier<String> {
|
||||
List<String> words = new ArrayList<>();
|
||||
Random rand = new Random(47);
|
||||
RandomWords(String fname) throws IOException {
|
||||
List<String> lines = Files.readAllLines(Paths.get(fname));
|
||||
// Skip the first line:
|
||||
for (String line : lines.subList(1, lines.size())) {
|
||||
for (String word : line.split("[ .?,]+"))
|
||||
words.add(word.toLowerCase());
|
||||
}
|
||||
}
|
||||
public String get() {
|
||||
return words.get(rand.nextint(words.size()));
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return words.stream()
|
||||
.collect(Collectors.joining(" "));
|
||||
}
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println(
|
||||
Stream.generate(new RandomWords("Cheese.dat"))
|
||||
.limit(10)
|
||||
.collect(Collectors.joining(" ")));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
输出为:
|
||||
|
||||
```java
|
||||
it shop sir the much cheese by conclusion district is
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Intermediate Operations -->
|
||||
|
||||
## 中间操作
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user