Stream 核心概念
Java 8 引入的 Stream API 彻底改变了集合处理方式,它提供了一种声明式、函数式的数据操作范式。Stream 并非数据结构,而是对数据源的元素序列进行计算的抽象视图。
核心接口体系
| 接口 |
功能定位 |
Stream<T> |
泛型对象流,支持顺序与并行聚合操作 |
IntStream |
原始 int 类型特化流,避免装箱开销 |
LongStream |
原始 long 类型特化流 |
DoubleStream |
原始 double 类型特化流 |
Collector<T,A,R> |
可变归约操作,用于将流元素累积到结果容器 |
Stream 的五大特性
- 无存储性:Stream 本身不保存数据,仅通过管道传输来自集合、数组、生成器或 I/O 通道的元素
- 不可变性:操作生成新 Stream 而非修改源数据,过滤操作不会删除原集合元素
- 惰性求值:中间操作延迟执行,终端操作触发实际计算,支持短路优化(如
findFirst)
- 无限可能性:配合
limit、findFirst 等短路操作可处理无限流
- 单次消费:元素仅能被访问一次,需重新创建 Stream 以重复遍历
Stream 创建方式详解
从集合构建
// 顺序流与并行流
List<String> words = Arrays.asList("alpha", "beta", "gamma");
Stream<String> sequential = words.stream();
Stream<String> parallel = words.parallelStream();
从数组构建
int[] scores = {85, 92, 78, 95};
IntStream scoreStream = Arrays.stream(scores);
String[] names = {"Alice", "Bob", "Carol"};
Stream<String> nameStream = Arrays.stream(names, 1, 3); // 指定范围
使用 Stream 工厂方法
// 固定元素流
Stream<String> colors = Stream.of("red", "green", "blue");
// 无限迭代流
Stream<BigInteger> powersOfTwo = Stream.iterate(
BigInteger.ONE,
n -> n.shiftLeft(1)
).limit(10);
// 无限生成流
Stream<UUID> randomIds = Stream.generate(UUID::randomUUID).limit(5);
其他数据源转换
// 正则分割
Pattern delimiter = Pattern.compile("\\s+");
Stream<String> tokens = delimiter.splitAsStream("hello world stream api");
// 文件行流
try (BufferedReader br = Files.newBufferedReader(Paths.get("data.txt"))) {
Stream<String> lines = br.lines();
// 处理逻辑...
}
核心操作实战
映射转换(map / flatMap)
class Product {
String name;
double price;
// 构造方法、getter 省略
}
List<Product> inventory = Arrays.asList(
new Product("Laptop", 8999.00),
new Product("Phone", 4999.00),
new Product("Tablet", 2999.00)
);
// 提取价格并计算折扣
List<Double> discountedPrices = inventory.stream()
.map(p -> p.getPrice() * 0.9)
.collect(Collectors.toList());
// flatMap 展平嵌套结构
List<List<Integer>> matrix = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5, 6),
Arrays.asList(7, 8, 9)
);
List<Integer> flattened = matrix.stream()
.flatMap(List::stream)
.collect(Collectors.toList()); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
过滤与匹配
List<Integer> values = Arrays.asList(10, 25, 30, 45, 50, 65);
// 多条件过滤
List<Integer> result = values.stream()
.filter(v -> v > 20)
.filter(v -> v % 10 == 5)
.collect(Collectors.toList()); // [25, 45, 65]
// 短路匹配
boolean hasEven = values.stream().anyMatch(v -> v % 2 == 0);
boolean allPositive = values.stream().allMatch(v -> v > 0);
boolean noNegative = values.stream().noneMatch(v -> v < 0);
排序与截断
List<String> cities = Arrays.asList("Shenzhen", "Beijing", "Shanghai", "Guangzhou");
// 自然排序与自定义比较器
List<String> byLength = cities.stream()
.sorted(Comparator.comparingInt(String::length))
.collect(Collectors.toList());
// 分页实现
int pageSize = 2;
int pageNum = 2; // 第2页
List<String> pageData = cities.stream()
.skip((pageNum - 1) * pageSize)
.limit(pageSize)
.collect(Collectors.toList()); // [Shanghai, Guangzhou]
归约与聚合
List<BigDecimal> amounts = Arrays.asList(
new BigDecimal("100.50"),
new BigDecimal("250.75"),
new BigDecimal("80.25")
);
// 求和
BigDecimal total = amounts.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 分组统计
Map<String, Long> categoryCount = inventory.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.counting()
));
// 分区
Map<Boolean, List<Product>> pricePartition = inventory.stream()
.collect(Collectors.partitioningBy(p -> p.getPrice() > 5000));
调试与副作用操作
// peek 用于调试,观察中间状态
List<Integer> processed = Stream.of(1, 2, 3, 4, 5)
.peek(n -> System.out.println("原始值: " + n))
.map(n -> n * n)
.peek(n -> System.out.println("平方后: " + n))
.filter(n -> n > 10)
.collect(Collectors.toList());
方法速查表
静态构造方法
| 方法 |
说明 |
Stream.of(T... values) |
由指定元素创建顺序流 |
Stream.iterate(T seed, UnaryOperator<T> f) |
迭代生成无限有序流 |
Stream.generate(Supplier<T> s) |
生成无限无序流 |
Stream.concat(Stream<T> a, Stream<T> b) |
延迟连接两个流 |
Stream.empty() |
创建空流 |
中间操作
| 方法 |
功能 |
filter(Predicate<T>) |
条件过滤 |
map(Function<T,R>) |
一对一映射 |
flatMap(Function<T,Stream<R>>) |
一对多展平映射 |
distinct() |
去重(基于 equals) |
sorted() / sorted(Comparator) |
排序 |
peek(Consumer<T>) |
检视元素 |
limit(long) |
截断保留前 n 个 |
skip(long) |
跳过前 n 个 |
终端操作
| 方法 |
功能 |
forEach(Consumer<T>) |
遍历消费 |
toArray() |
转为数组 |
reduce(BinaryOperator<T>) |
归约聚合 |
collect(Collector<T,A,R>) |
收集到容器 |
min/max(Comparator<T>) |
最值 |
count() |
计数 |
anyMatch/allMatch/noneMatch |
匹配判断 |
findFirst/findAny |
查找元素 |