List bubbles = Arrays.asList(new Bubble(1), new Bubble(2), new Bubble(3));
System.out.println(bubbles.stream()
.mapToInt(b -> b.i)
.sum());
Set 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);
}
}
输出结果:
6
a pie! It’s for wonderful day
phi: 1.618
e: 2.718
pi: 3.14159
-
创建
List<Bubble>
对象后,只需简单调用所有集合中都有的stream()
。 -
中间操作
map()
会获取流中的所有元素,并且对流中元素应用操作从而产生新的元素,并将其传递到后续的流中。通常map()
会获取对象并产生新的对象,但在这里产生了特殊的用于数值类型的流。例如,mapToInt()
方法将一个对象流(object stream)转换成为包含整型数字的IntStream
。 -
通过调用字符串的
split()
来获取元素用于定义变量w
。 -
为了从 Map 集合中产生流数据,我们首先调用
entrySet()
产生一个对象流,每个对象都包含一个key
键以及与其相关联的value
值。然后分别调用getKey()
和getValue()
获取值。
[](()随机数流
Random
类被一组生成流的方法增强了。代码示例:
// streams/RandomGenerators.java
import java.util.*;
import java.util.stream.*;
public class RandomGenerators {
public static void show(Stream 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());
// 控制上限和下限:
show(rand.ints(10, 20).boxed());
show(rand.longs(50, 100).boxed());
show(rand.doubles(20, 30).boxed());
// 控制流大小:
show(rand.ints(2).boxed());
show(rand.longs(2).boxed());
show(rand.doubles(2).boxed());
// 控制流的大小和界限
show(rand.ints(3, 3, 9).boxed());
show(rand.longs(3, 12, 22).boxed());
show(rand.doubles(3, 11.5, 12.3).boxed());
}
}
输出结果:
-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。如下是一个文本文件提供字符串对象的例子。
Cheese.dat 文件内容:
// 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.
我们通过 File 类将 Cheese.dat 文件的所有行读取到 List<String>
中。代码示例:
// 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 {
List words = new ArrayList<>();
Random rand = new Random(47);
RandomWords(String fname) throws IOException {
List lines = Files.readAllLines(Paths.get(fname));
// 略过第一行
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(" ")));
}
}
输出结果:
it shop sir the much cheese by conclusion district is
在这里你可以看到更为复杂的 split()
运用。在构造器中,每一行都被 split()
通过空格或者被方括号包裹的任意标点符号进行分割。在结束方括号后面的 +
代表 +
前面的东西可以出现一次或者多次。
我们注意到在构造函数中循环体使用命令式编程(外部迭代)。在以后的例子中,你甚至会看到我们如何消除这一点。这种旧的形式虽不是特别糟糕,但使用流会让人感觉更好。
在 toString()
和主方法中你看到了 collect()
收集操作,它根据参数来组合所有流中的元素。
当你使用 Collectors.joining()
,你将会得到一个 String
类型的结果,每个元素都根据 joining()
的参数来进行分割。还有许多不同的 Collectors
用于产生不同的结果。
在主方法中,我们提前看到了 Stream.generate()
的用法,它可以把任意 Supplier<T>
用于生成 T
类型的流。
[](()int 类型的范围
IntStream
类提供了 range()
方法用于生成整型序列的流。编写循环时,这个方法会更加便利:
// streams/Ranges.java
import static java.util.stream.IntStream.*;
public class Ranges {
public static void main(String[] args) {
// 传统方法:
int result = 0;
for (int i = 10; i < 20; i++)
result += i;
System.out.println(result);
// for-in 循环:
result = 0;
for (int i : range(10, 20).toArray())
result += i;
System.out.println(result);
// 使用流:
System.out.println(range(10, 20).sum());
}