它是什么

Java 8 引入的"集合 + 函数式"组合——声明式处理数据:

List<Integer> result = nums.stream()
    .filter(n -> n > 2)
    .map(n -> n * 10)
    .sorted()
    .toList();

不是新集合类型——是操作管道。流式调用 + 末尾 collect / count / forEach 触发。

创建 Stream

List.of(1, 2, 3).stream();
Stream.of(1, 2, 3);
Arrays.stream(new int[]{1, 2, 3});          // IntStream
IntStream.range(0, 100);                     // 0..99
IntStream.rangeClosed(0, 100);                // 0..100
Stream.iterate(1, n -> n * 2).limit(10);     // 1, 2, 4, 8, ..., 512
Files.lines(path);                            // 文件每行(lazy)

主要中间操作

.filter(predicate)              // 过滤
.map(function)                   // 转换
.mapToInt(...) / .mapToLong(...) / .mapToDouble(...)  // 转基本类型流
.flatMap(function)               // 扁平化(一对多)
.sorted() / .sorted(comparator) // 排序
.distinct()                      // 去重
.limit(n)                        // 取前 n
.skip(n)                         // 跳前 n
.peek(consumer)                  // 偷看(调试用)

终止操作

.collect(...)                    // 聚合到集合
.toList()                        // Java 16+,简化版
.toArray()
.count()
.findFirst() / .findAny()        // 返 Optional
.anyMatch(p) / .allMatch(p) / .noneMatch(p)
.min(comparator) / .max(comparator)
.reduce(...)                     // 折叠
.forEach(consumer)
.forEachOrdered(consumer)        // 并行流时保顺序
.sum() / .average() / .min() / .max()   // IntStream/LongStream/DoubleStream

常见模式

过滤 + 转换 + 收集

List<String> adultNames = users.stream()
    .filter(u -> u.getAge() >= 18)
    .map(User::getName)
    .sorted()
    .toList();

分组(GroupBy)

Map<String, List<User>> byDept = users.stream()
    .collect(Collectors.groupingBy(User::getDepartment));

// 分组 + 计数
Map<String, Long> countByDept = users.stream()
    .collect(Collectors.groupingBy(User::getDepartment, Collectors.counting()));

// 分组 + 求和
Map<String, Integer> totalSalary = users.stream()
    .collect(Collectors.groupingBy(
        User::getDepartment,
        Collectors.summingInt(User::getSalary)
    ));

转 Map

Map<Integer, String> idToName = users.stream()
    .collect(Collectors.toMap(User::getId, User::getName));

// 处理重复 key
Map<Integer, String> dedup = users.stream()
    .collect(Collectors.toMap(
        User::getId, User::getName,
        (existing, replacement) -> existing      // 冲突时保留原值
    ));

Join 字符串

String csv = users.stream()
    .map(User::getName)
    .collect(Collectors.joining(", "));
// "Alice, Bob, Charlie"

String wrapped = users.stream()
    .map(User::getName)
    .collect(Collectors.joining(", ", "[", "]"));
// "[Alice, Bob, Charlie]"

数值聚合(IntStream)

int sum = users.stream().mapToInt(User::getAge).sum();
double avg = users.stream().mapToInt(User::getAge).average().orElse(0);
IntSummaryStatistics stats = users.stream().mapToInt(User::getAge).summaryStatistics();
// stats.getSum() / getAverage() / getMin() / getMax() / getCount()

flatMap:扁平化

List<List<Integer>> nested = List.of(
    List.of(1, 2),
    List.of(3, 4, 5),
    List.of(6)
);

List<Integer> flat = nested.stream()
    .flatMap(List::stream)
    .toList();
// [1, 2, 3, 4, 5, 6]

reduce:折叠

int sum = nums.stream().reduce(0, Integer::sum);          // 带初始值
Optional<Integer> max = nums.stream().reduce(Integer::max); // 无初始值(空时返 Optional.empty)

String concat = strs.stream().reduce("", String::concat);

通常 sum / min / max / joining 已经能解决——reduce 在自定义聚合时用。

并行流(parallelStream)

int sum = nums.parallelStream().mapToInt(Integer::intValue).sum();

慎用

  • 共享 ForkJoinPool(默认 Common Pool,所有 parallelStream 抢)
  • 小数据更慢(线程调度开销)
  • 操作要无副作用、可结合(commutative)
  • 在 Web 容器 / 线程池里用容易出意外

经验:不剖析不并行——只有证明慢、且数据量大(10k+)、操作 CPU 重时考虑。日常用串行流就够。

流是一次性的

var s = list.stream().filter(...);
s.toList();        // ✅
s.toList();        // ❌ IllegalStateException: stream has already been operated upon

每次需要新流——再调一次 .stream()

不要在流里改外部状态

List<Integer> result = new ArrayList<>();
nums.stream()
    .filter(n -> n > 0)
    .forEach(result::add);    // ❌ 副作用,并行流时会出错
List<Integer> result = nums.stream()    // ✅ 用 collect
    .filter(n -> n > 0)
    .toList();

Java 16+ 的简化

.toList()           // 替代 .collect(Collectors.toList()),返不可变 List
.mapMulti(...)      // flatMap 的更高效版本(Java 16+)

// Stream.toList() 返回的是不可变 List
list.stream().toList().add(1);    // ❌ UnsupportedOperationException

性能 vs 普通 for

// for 循环
int sum = 0;
for (int n : nums) if (n > 0) sum += n;

// stream
int sum2 = nums.stream().filter(n -> n > 0).mapToInt(Integer::intValue).sum();

stream 通常比 for 慢 2-5 倍——但对 99% 业务代码无意义(差几微秒)。优先用 stream 提高可读性

热点循环 / 1M+ 元素 → 写 for。

Files.lines + Stream

try (var lines = Files.lines(Path.of("data.txt"))) {
    long count = lines.filter(l -> !l.isBlank()).count();
}

注意 try-with-resources——流持有文件句柄,必须关闭。

→ 下一篇 Optional