44# 第十四章 流式编程
55
66<!-- Java 8 Stream Support -->
7+
78## 流支持
89
910Java 设计者面临着一个难题。他们拥有着一系列已经存在的库,这些库不仅仅在 Java 库本身中所使用,并且被用户编写的数百万行代码中使用。那他们如何将一个全新的流的基本概念融入到已经存在的库中?
@@ -19,8 +20,239 @@ Java 8 中引入的解决方案是在[接口](10-Interfaces.md)中添加 **defau
1920<!-- Stream Creation -->
2021## 流创建
2122
23+ 你可以通过 ** Stream.of()** 很容易的将一组元素转发成为流(** Bubble** 类在之前的章节中已经定义过了):
24+
25+ ``` java
26+ // streams/StreamOf.java
27+ import java.util.stream.* ;
28+ public class StreamOf {
29+ public static void main (String [] args ) {
30+ Stream . of(new Bubble (1 ), new Bubble (2 ), new Bubble (3 ))
31+ .forEach(System . out:: println);
32+ Stream . of(" It's " , " a " , " wonderful " , " day " , " for " , " pie!" )
33+ .forEach(System . out:: print);
34+ System . out. println();
35+ Stream . of(3.14159 , 2.718 , 1.618 )
36+ .forEach(System . out:: println);
37+ }
38+ }
39+ ```
40+
41+ 输出为:
42+
43+ ``` java
44+ Bubble(1 )
45+ Bubble(2 )
46+ Bubble(3 )
47+ It ' s a wonderful day for pie!
48+ 3.14159
49+ 2.718
50+ 1.618
51+ ```
52+
53+ 除此之外,每个 **Collection** 都可以通过 **stream()** 方法来产生一个流:
54+
55+ ```java
56+ // streams/CollectionToStream.java
57+ import java.util.*;
58+ import java.util.stream.*;
59+ public class CollectionToStream {
60+ public static void main(String[] args) {
61+ List<Bubble> bubbles = Arrays.asList(new Bubble(1), new Bubble(2), new Bubble(3));
62+ System.out.println(bubbles.stream()
63+ .mapToint(b -> b.i)
64+ .sum());
65+
66+ Set<String> w = new HashSet<>(Arrays.asList("It' s a wonderful day for pie! " .split(" " )));
67+ w.stream()
68+ .map(x -> x + " " )
69+ .forEach(System.out::print);
70+ System.out.println();
71+
72+ Map<String, double> m = new HashMap<>();
73+ m.put(" pi" , 3.14159);
74+ m.put(" e" , 2.718);
75+ m.put(" phi" , 1.618);
76+ m.entrySet().stream()
77+ .map(e -> e.getKey() + " : " + e.getValue())
78+ .forEach(System.out::println);
79+ }
80+ }
81+ ```
82+
83+ 输出为:
84+
85+ ```java
86+ 6
87+ a pie! It's for wonderful day
88+ phi: 1.618
89+ e: 2.718
90+ pi: 3.14159
91+ ```
92+
93+ 在创建 **List\< Bubble\> ** 对象之后,我们只需要简单的调用所有集合中都有的方法 **stream()**。中间操作 **map()** 会获取流中的所有元素,并且对流中元素应用操作从而产生新的元素,并将其传递到流中。通常情况 **map()** 方法获取对象并产生新的对象,但是这里有特殊版本的方法用于数值类型的流。例如,**mapToInt()** 方法将一个对象流(objects stream)转换成为包含整形数字的 **IntStream**。同样有针对 **Float** 和 **Double** 的类似名字的操作。
94+
95+ 我们通过在 **String** 类型上面应用 **split()** - split 方法会根据参数来拆分字符串 - 获取元素用于定义 **w**。稍后你会看到这个参数十分复杂,但是在这里我们只是根据空格来分割字符串。
96+
97+ 为了从 **Map** 集合中产生流数据,我们首先调用 **entrySet()** 去产生一个对象流,每个对象都包含一个键以及与其相关联的值。然后调用 **getKey()** 和 **getValue()** 将其分开。
98+
99+ ### 随机数流
100+
101+ **Random** 类被一组生成流的方法增强了:
102+
103+ ```java
104+ // streams/RandomGenerators.java
105+ import java.util.*;
106+ import java.util.stream.*;
107+ public class RandomGenerators {
108+ public static <T> void show(Stream<T> stream) {
109+ stream
110+ .limit(4)
111+ .forEach(System.out::println);
112+ System.out.println(" ++++++++ " );
113+ }
114+
115+ public static void main(String[] args) {
116+ Random rand = new Random(47);
117+ show(rand.ints().boxed());
118+ show(rand.longs().boxed());
119+ show(rand.doubles().boxed());
120+ // Control the lower and upper bounds:
121+ show(rand.ints(10, 20).boxed());
122+ show(rand.longs(50, 100).boxed());
123+ show(rand.doubles(20, 30).boxed());
124+ // Control the stream size:
125+ show(rand.ints(2).boxed());
126+ show(rand.longs(2).boxed());
127+ show(rand.doubles(2).boxed());
128+ // Control the stream size and bounds:
129+ show(rand.ints(3, 3, 9).boxed());
130+ show(rand.longs(3, 12, 22).boxed());
131+ show(rand.doubles(3, 11.5, 12.3).boxed());
132+ }
133+ }
134+ ```
135+
136+ 输出为:
137+
138+ ```java
139+ -1172028779
140+ 1717241110
141+ -2014573909
142+ 229403722
143+ ++++++++
144+ 2955289354441303771
145+ 3476817843704654257
146+ -8917117694134521474
147+ 4941259272818818752
148+ ++++++++
149+ 0.2613610344283964
150+ 0.0508673570556899
151+ 0.8037155449603999
152+ 0.7620665811558285
153+ ++++++++
154+ 16
155+ 10
156+ 11
157+ 12
158+ ++++++++
159+ 65
160+ 99
161+ 54
162+ 58
163+ ++++++++
164+ 29.86777681078574
165+ 24.83968447804611
166+ 20.09247112332014
167+ 24.046793846338723
168+ ++++++++
169+ 1169976606
170+ 1947946283
171+ ++++++++
172+ 2970202997824602425
173+ -2325326920272830366
174+ ++++++++
175+ 0.7024254510631527
176+ 0.6648552384607359
177+ ++++++++
178+ 6
179+ 7
180+ 7
181+ ++++++++
182+ 17
183+ 12
184+ 20
185+ ++++++++
186+ 12.27872414236691
187+ 11.732085449736195
188+ 12.196509449817267
189+ ++++++++
190+ ```
191+
192+ 为了消除冗余代码,我创建了一个泛型方法 **show(Stream\< T\> stream)** (在讲解泛型之前就使用这个特性,确实有点作弊,但是回报是值得的)。类型参数 **T** 可以是任何类型,所以这个方法对 **Integer**, **Long** 和 **Double** 类型都生效。但是 **Random** 类只能生成原始数据类型 **int**, **long**, **double** 的流。幸运的是, **boxed()** 流操作将会自动的把基本类型包装成为对应的装箱类型,从而使得 **show()** 能够接受流。
193+
194+ 我们可以使用 **Random** 为任意对象集合创建 **Supplier**。如下是一个从文本文件提供 **String** 对象的例子:
195+
196+ ```java
197+ // streams/Cheese.dat
198+ Not much of a cheese shop really, is it?
199+ Finest in the district, sir.
200+ And what leads you to that conclusion?
201+ Well, it's so clean.
202+ It's certainly uncontaminated by cheese.
203+ We use the Files class to read all the lines from a file into a
204+ List<String> :
205+ ```
206+
207+ ```java
208+ // streams/RandomWords.java
209+ import java.util.*;
210+ import java.util.stream.*;
211+ import java.util.function.*;
212+ import java.io.*;
213+ import java.nio.file.*;
214+ public class RandomWords implements Supplier<String> {
215+ List<String> words = new ArrayList<>();
216+ Random rand = new Random(47);
217+ RandomWords(String fname) throws IOException {
218+ List<String> lines = Files.readAllLines(Paths.get(fname));
219+ // Skip the first line:
220+ for (String line : lines.subList(1, lines.size())) {
221+ for (String word : line.split(" [ . ? ,]+ " ))
222+ words.add(word.toLowerCase());
223+ }
224+ }
225+ public String get() {
226+ return words.get(rand.nextint(words.size()));
227+ }
228+ @Override
229+ public String toString() {
230+ return words.stream()
231+ .collect(Collectors.joining(" " ));
232+ }
233+ public static void main(String[] args) throws Exception {
234+ System.out.println(
235+ Stream.generate(new RandomWords(" Cheese . dat" ))
236+ .limit(10)
237+ .collect(Collectors.joining(" " )));
238+ }
239+ }
240+ ```
241+
242+ 输出为:
243+
244+ ```java
245+ it shop sir the much cheese by conclusion district is
246+ ```
247+
248+
249+
250+
251+
252+
22253
23254<!-- Intermediate Operations -->
255+
24256## 中间操作
25257
26258
0 commit comments