Stream 流
面试提问
"Java Stream 有哪些常用操作?map 和 flatMap 有什么区别?"
Stream 简介
Stream 是 Java 8 引入的用于处理集合数据的 API,支持串行和并行操作。
特点
| 特点 | 说明 |
|---|---|
| 声明式 | 描述做什么,而不是怎么做 |
| 链式操作 | 操作可链接,代码简洁 |
| 惰性求值 | 中间操作不会立即执行 |
| 一次性 | Stream 只能消费一次 |
| 可能并行 | 支持并行处理 |
创建 Stream
// 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> s1 = list.stream();
Stream<String> s2 = list.parallelStream(); // 并行流
// 从数组创建
String[] arr = {"a", "b", "c"};
Stream<String> s3 = Arrays.stream(arr);
// 使用 Stream.of()
Stream<String> s4 = Stream.of("a", "b", "c");
// 无限流
Stream<Integer> s5 = Stream.iterate(0, n -> n + 1); // 0, 1, 2, ...
Stream<Double> s6 = Stream.generate(Math::random); // 随机数流Stream 操作分类
中间操作(返回新 Stream)
| 操作 | 说明 |
|---|---|
filter | 过滤 |
map | 映射 |
flatMap | 扁平化映射 |
distinct | 去重 |
sorted | 排序 |
limit | 限制数量 |
skip | 跳过前 n 个 |
peek | 查看元素(调试用) |
终端操作(返回结果)
| 操作 | 说明 |
|---|---|
collect | 收集到集合 |
forEach | 遍历 |
count | 计数 |
reduce | 归约 |
min/max | 最小/最大值 |
anyMatch | 任一匹配 |
allMatch | 全部匹配 |
noneMatch | 无匹配 |
findFirst | 找第一个 |
findAny | 找任意一个 |
toArray | 转数组 |
常用操作示例
filter(过滤)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 过滤偶数
List<Integer> evens = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// [2, 4, 6]map(映射)
List<String> words = Arrays.asList("hello", "world");
// 转大写
List<String> upper = words.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// ["HELLO", "WORLD"]
// 获取长度
List<Integer> lengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
// [5, 5]flatMap(扁平化)
List<List<Integer>> nested = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6)
);
// map:每个元素变成一个 List
List<Stream<Integer>> r1 = nested.stream()
.map(List::stream)
.collect(Collectors.toList());
// flatMap:扁平化为一个流
List<Integer> flat = nested.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// [1, 2, 3, 4, 5, 6]
// 字符串拆分为字符
List<String> words = Arrays.asList("hello", "world");
List<String> chars = words.stream()
.flatMap(s -> Arrays.stream(s.split("")))
.distinct()
.collect(Collectors.toList());
// ["h", "e", "l", "o", "w", "r", "d"]map vs flatMap
| 操作 | 输入 | 输出 |
|---|---|---|
map | T -> R | Stream<R> |
flatMap | T -> Stream<R> | Stream<R>(扁平化) |
// map:一对一
["hello", "world"] --map(String::length)--> [5, 5]
// flatMap:一对多,然后扁平化
["hello", "world"] --flatMap(s -> split(s))--> ["h","e","l","l","o","w","o","r","l","d"]sorted(排序)
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);
// 自然排序
List<Integer> sorted = numbers.stream()
.sorted()
.collect(Collectors.toList());
// [1, 1, 3, 4, 5, 9]
// 自定义排序
List<Integer> descSorted = numbers.stream()
.sorted((a, b) -> b - a)
.collect(Collectors.toList());
// [9, 5, 4, 3, 1, 1]
// 去重后排序
List<Integer> distinctSorted = numbers.stream()
.distinct()
.sorted()
.collect(Collectors.toList());
// [1, 3, 4, 5, 9]reduce(归约)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 求和
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b); // 15
// 求和(无初始值,返回 Optional)
Optional<Integer> sum2 = numbers.stream()
.reduce((a, b) -> a + b);
// 求最大值
Optional<Integer> max = numbers.stream()
.reduce(Integer::max);
// 字符串拼接
List<String> words = Arrays.asList("Hello", "World");
String result = words.stream()
.reduce("", (a, b) -> a + " " + b); // " Hello World"Collectors 工具类
收集到集合
List<String> list = Stream.of("a", "b", "c")
.collect(Collectors.toList());
Set<String> set = Stream.of("a", "b", "a")
.collect(Collectors.toSet());
LinkedList<String> linkedList = Stream.of("a", "b", "c")
.collect(Collectors.toCollection(LinkedList::new));收集到 Map
List<User> users = Arrays.asList(
new User(1, "Alice"),
new User(2, "Bob")
);
// toMap(keyMapper, valueMapper)
Map<Integer, String> map1 = users.stream()
.collect(Collectors.toMap(User::getId, User::getName));
// {1=Alice, 2=Bob}
// 处理 key 冲突
Map<String, Integer> map2 = users.stream()
.collect(Collectors.toMap(
User::getName,
User::getId,
(old, newVal) -> old // 保留旧值
));分组
List<User> users = Arrays.asList(
new User("Alice", "IT"),
new User("Bob", "HR"),
new User("Charlie", "IT")
);
// 按部门分组
Map<String, List<User>> groupByDept = users.stream()
.collect(Collectors.groupingBy(User::getDept));
// {IT=[Alice, Charlie], HR=[Bob]}
// 分组并计数
Map<String, Long> countByDept = users.stream()
.collect(Collectors.groupingBy(
User::getDept,
Collectors.counting()
));
// {IT=2, HR=1}分区
// 按条件分为两组
Map<Boolean, List<Integer>> partitioned = Stream.of(1, 2, 3, 4, 5, 6)
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
// {false=[1, 3, 5], true=[2, 4, 6]}统计
IntSummaryStatistics stats = Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.summarizingInt(Integer::intValue));
stats.getCount(); // 5
stats.getSum(); // 15
stats.getAverage(); // 3.0
stats.getMax(); // 5
stats.getMin(); // 1
// 单独统计
int sum = Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.summingInt(Integer::intValue));
double avg = Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.averagingInt(Integer::intValue));连接字符串
String joined = Stream.of("a", "b", "c")
.collect(Collectors.joining()); // "abc"
String joined2 = Stream.of("a", "b", "c")
.collect(Collectors.joining(", ")); // "a, b, c"
String joined3 = Stream.of("a", "b", "c")
.collect(Collectors.joining(", ", "[", "]")); // "[a, b, c]"并行流
创建并行流
// 从集合
Stream<String> parallel1 = list.parallelStream();
// 从串行流转并行流
Stream<String> parallel2 = list.stream().parallel();并行流注意事项
// 可能的问题
List<Integer> list = new ArrayList<>();
IntStream.range(0, 10000).parallel().forEach(list::add);
// ArrayList 不是线程安全的,结果可能小于 10000
// 解决方案1:使用线程安全集合
List<Integer> safeList = Collections.synchronizedList(new ArrayList<>());
// 解决方案2:使用 collect
List<Integer> result = IntStream.range(0, 10000)
.parallel()
.boxed()
.collect(Collectors.toList());何时使用并行流
| 场景 | 是否适合并行 |
|---|---|
| 数据量大 | ✅ 适合 |
| 计算密集 | ✅ 适合 |
| IO 密集 | ❌ 不适合 |
| 顺序依赖 | ❌ 不适合 |
| 共享可变状态 | ❌ 不适合 |
面试要点总结
| 问题 | 答案要点 |
|---|---|
| Stream 特点? | 声明式、链式、惰性、一次性、可并行 |
| 中间 vs 终端操作? | 中间返回 Stream,终端返回结果 |
| map vs flatMap? | map 一对一,flatMap 一对多并扁平化 |
| 常用 Collectors? | toList、toMap、groupingBy、joining |
| 并行流注意? | 避免共享可变状态,数据量大时才用 |
参考资料
- Java Stream API (opens in a new tab)
- 《Java 8 实战》