知识模块
☕ Java 知识模块
六、Java 新特性
Stream 流

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

操作输入输出
mapT -> RStream<R>
flatMapT -> 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
并行流注意?避免共享可变状态,数据量大时才用

参考资料