概述
Stream API 是 Java 中引入的一种新的数据处理方法。它提供了一种高效且易于使用的方法来处理数据集合。Stream API 支持函数式编程,可以让我们以简洁、优雅的方式进行数据操作,还有使用 Stream 的两大原因:
- 在大多数情况下,将对象存储在集合中就是为了处理它们,因此你会发现你把编程 的主要焦点从集合转移到了流上。
- 当 Lambda 表达式和方法引用(method references),流(Stream)结合使用的时候会让人感觉自成一体,行云流水的感觉
先展示一段简单的流式编程:
import java.util.Random;
public class Randoms {
public static void main(String[] args) {
// 随机展示 5 至 20 之间不重复的整数并进行排序
new Random(47)
.ints(5, 20)
.distinct() // 使流中的整数不重复
.limit(7) // 获取前 7 个元素
.sorted() // 排序
.forEach(System.out::println);
}
}
输出结果:
6
10
13
16
17
18
19
实际上函数式的编程风格是声明式(Declarative programming)的,它声明了要做什么, 而不是指明(每一步)如何做。
相同的程序,相比声明式风格,命令式(Imperative)编程的形式(指明每一步如何做),代码阅读起来会更难理解:
import java.util.Random;
import java.util.SortedSet;
import java.util.TreeSet;
public class ImperativeRandoms {
public static void main(String[] args) {
Random rand = new Random(47);
SortedSet<Integer> rints = new TreeSet<>();
while (rints.size() < 7) {
int r = rand.nextInt(20);
if (r < 5) continue;
rints.add(r);
}
System.out.println(rints);
}
}
输出结果:
[7, 8, 9, 11, 13, 15, 18]
所以使用流式编程的几个理由:
- 表达力强,清晰的语义
- 内部迭代 internal iteration (看不见迭代过程)更简单的处理并发
- 流式懒加载的,只在绝对必要时才计算
Java 8 通过在添加接口中添加 default 关键字,通过默认方法的方式将流式 Stream 方法平滑地嵌入到现有的类中,
流操作的类型有三种:
- 创建流:生产流
- 修改流元素:中间操作
- 消费流元素:终端操作,收集流元素,通常式汇入一个集合
创建流
通过 Stream.of()
很容见的将一组元素转化为流:
import java.util.stream.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);
}
}
输出结果:
Bubble 1
Bubble 2
Bubble 3
It's a wonderful day for pie!
3.14159
2.718
1.618
通过 stream()
方法很容易将传统的集合转化为 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) // 获取流中所有元素,对元素进行应用操作,并产生新的对象,这里的 mapToInt 中间操作会转换成为包含整型数字的 IntStream
.sum()); // 合计
HashSet<String> w = new HashSet<>(Arrays.asList("It's a wonderful day for pie!".split(" ")));
w.stream()
.map(x -> x + " ")
.forEach(System.out::print); // stream 遍历并且打印 Set 中的元素
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); // stream 遍历并且打印 Map 中的元素
}
}
输出结果:
6
a pie! It's for wonderful day
phi: 1.618
e: 2.718
pi: 3.14159
随机数流
Java 8 的 Random
类也集成流的方法,很方便的创建随机数流:
import java.util.Random;
import java.util.stream.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());
// 控制上限和下限
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
++++++++++
# ……………………
int 整型范围
Stream API 对基本数据类型生成流提供便捷的方法,例如对一段整型序列求和,展示新旧代码对比
import java.util.stream.IntStream;
public class Ranges {
public static void main(String[] args) {
// 传统方法
int result = 0;
for (int i = 0; i < 20; i++) {
result += i;
}
System.out.println(result);
// 使用流
System.out.println(IntStream.range(0, 20).sum());
}
}
输出结果:
190
190
generate()
Stream API 还可以结合 Supplier 函数接口来创建流,例如,创建一个随机数序列:
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamGenerateExample {
public static void main(String[] args) {
Random random = new Random();
Stream<Integer> randomNumbers = Stream.generate(random::nextInt);
// 生成 10 个随机数放入集合中
List<Integer> integers = randomNumbers
.limit(10)
.collect(Collectors.toList());
integers.forEach(System.out::println);
}
}
输出结果:
514000574
1771591868
600289224
-1474939200
-276604430
-876159270
509964750
-497958443
811408347
703285366
iterate()
Stream.iterate
是 Java 8 引入的 Stream API 的一部分,它接受一个种子值(seed)和一个一元函数(unary operator),然后生成一个无限的、顺序的流。流中的每个元素都是通过对前一个元素应用一元函数生成的。与