Stream
Stream 是 Java 8 引入的数据处理管道,对集合进行函数式风格的链式操作,不修改源数据,支持串行和并行两种模式。
创建 Stream
// 从集合
List<String> list = List.of("a", "b", "c");
Stream<String> s1 = list.stream();
Stream<String> s2 = list.parallelStream(); // 并行流
// 直接创建
Stream<Integer> s3 = Stream.of(1, 2, 3);
// 空流
Stream<String> empty = Stream.empty();
// 数组
Stream<String> s4 = Arrays.stream(new String[]{"a", "b"});
// 无限流
Stream<Integer> iterate = Stream.iterate(0, n -> n + 2); // 0,2,4,6...
Stream<Double> generate = Stream.generate(Math::random);
// 数值专用流(避免装箱开销)
IntStream ints = IntStream.range(1, 6); // 1~5
IntStream ints2 = IntStream.rangeClosed(1, 5); // 1~5
LongStream longs = LongStream.of(1L, 2L, 3L);
DoubleStream doubles = DoubleStream.of(1.0, 2.0);中间操作(惰性,返回新 Stream)
过滤与截断
Stream.of(1, 2, 3, 4, 5)
.filter(n -> n % 2 == 0) // [2, 4]
.limit(1) // [2]
.skip(0); // [2]
// takeWhile / dropWhile(Java 9+,有序流)
Stream.of(1, 2, 3, 4, 5)
.takeWhile(n -> n < 4) // [1, 2, 3],遇到不满足立即停止
Stream.of(1, 2, 3, 4, 5)
.dropWhile(n -> n < 4); // [4, 5],跳过满足条件的前缀转换
// map:一对一转换
Stream.of("hello", "world")
.map(String::toUpperCase); // ["HELLO", "WORLD"]
// mapToInt / mapToLong / mapToDouble:转为数值流
Stream.of("1", "2", "3")
.mapToInt(Integer::parseInt)
.sum(); // 6
// flatMap:一对多,展平嵌套
Stream.of(List.of(1, 2), List.of(3, 4))
.flatMap(Collection::stream); // [1, 2, 3, 4]
Stream.of("hello world", "foo bar")
.flatMap(s -> Arrays.stream(s.split(" ")));
// ["hello", "world", "foo", "bar"]排序与去重
Stream.of(3, 1, 4, 1, 5, 9)
.distinct() // [3, 1, 4, 5, 9]
.sorted() // [1, 3, 4, 5, 9]
.sorted(Comparator.reverseOrder()); // [9, 5, 4, 3, 1]
// 按对象属性排序
users.stream()
.sorted(Comparator.comparing(User::getAge))
.sorted(Comparator.comparing(User::getAge).reversed())
.sorted(Comparator.comparing(User::getAge)
.thenComparing(User::getName));调试
Stream.of(1, 2, 3)
.peek(n -> System.out.println("before: " + n))
.map(n -> n * 2)
.peek(n -> System.out.println("after: " + n))
.collect(Collectors.toList());终止操作(触发执行)
收集
List<String> names = users.stream()
.map(User::getName)
.collect(Collectors.toList());
// Java 16+
List<String> names2 = users.stream()
.map(User::getName)
.toList(); // 不可变 List
Set<String> nameSet = users.stream()
.map(User::getName)
.collect(Collectors.toSet());
// 指定具体实现类
LinkedList<String> linked = users.stream()
.collect(Collectors.toCollection(LinkedList::new));
// 拼接字符串
String joined = Stream.of("a", "b", "c")
.collect(Collectors.joining(", ", "[", "]")); // "[a, b, c]"分组与分区
// 按部门分组
Map<String, List<User>> byDept = users.stream()
.collect(Collectors.groupingBy(User::getDept));
// 分组后统计数量
Map<String, Long> countByDept = users.stream()
.collect(Collectors.groupingBy(User::getDept, Collectors.counting()));
// 分组后取名字列表
Map<String, List<String>> namesByDept = users.stream()
.collect(Collectors.groupingBy(
User::getDept,
Collectors.mapping(User::getName, Collectors.toList())));
// 多级分组
Map<String, Map<Integer, List<User>>> grouped = users.stream()
.collect(Collectors.groupingBy(User::getDept,
Collectors.groupingBy(User::getAge)));
// 分区(按 true/false 分为两组)
Map<Boolean, List<User>> partitioned = users.stream()
.collect(Collectors.partitioningBy(u -> u.getAge() >= 18));转为 Map
// toMap(key 冲突时抛异常)
Map<Long, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, u -> u));
// key 冲突时保留后者
Map<String, User> byName = users.stream()
.collect(Collectors.toMap(
User::getName,
u -> u,
(existing, replacement) -> replacement));
// 保持插入顺序
Map<Long, String> ordered = users.stream()
.collect(Collectors.toMap(
User::getId,
User::getName,
(a, b) -> a,
LinkedHashMap::new));规约与统计
Stream<Integer> nums = Stream.of(1, 2, 3, 4, 5);
// reduce
Optional<Integer> sum = nums.reduce(Integer::sum); // Optional<Integer>
int sum2 = nums.reduce(0, Integer::sum); // 有初始值,返回 int
// 数值流统计
IntSummaryStatistics stats = IntStream.range(1, 6).summaryStatistics();
stats.getSum(); // 15
stats.getAverage(); // 3.0
stats.getMax(); // 5
stats.getMin(); // 1
stats.getCount(); // 5
// 快捷方法(数值流)
IntStream.range(1, 6).sum();
IntStream.range(1, 6).average(); // OptionalDouble
IntStream.range(1, 6).max(); // OptionalInt查找与匹配
Stream<Integer> s = Stream.of(1, 2, 3, 4, 5);
s.anyMatch(n -> n > 3); // true(任一满足)
s.allMatch(n -> n > 0); // true(全部满足)
s.noneMatch(n -> n > 10); // true(全不满足)
s.findFirst(); // Optional<Integer> 第一个
s.findAny(); // Optional<Integer> 任意(并行流中更快)
s.count(); // 5L遍历
users.stream().forEach(System.out::println);
// 有序遍历(并行流中也保证顺序,性能略低)
users.parallelStream().forEachOrdered(System.out::println);并行流
// 开启并行
list.parallelStream()
.filter(...)
.map(...)
.collect(Collectors.toList());
// 或在中间切换
stream.parallel()
stream.sequential()注意事项:
- 并行流使用
ForkJoinPool.commonPool(),默认线程数 = CPU 核心数 - 1 - 数据量小时并行反而更慢(线程调度开销)
forEach在并行流中顺序不定,需要有序时用forEachOrdered- 共享可变状态会导致线程安全问题,收集时用线程安全的 Collector
自定义 Collector
// 示例:收集到不可变 Map
Collector<User, ?, Map<Long, String>> toUnmodifiableMap =
Collectors.collectingAndThen(
Collectors.toMap(User::getId, User::getName),
Collections::unmodifiableMap);
Map<Long, String> result = users.stream().collect(toUnmodifiableMap);常用链式示例
// 过滤 + 转换 + 收集
List<String> adultNames = users.stream()
.filter(u -> u.getAge() >= 18)
.map(User::getName)
.sorted()
.collect(Collectors.toList());
// 求平均年龄
OptionalDouble avgAge = users.stream()
.mapToInt(User::getAge)
.average();
// 按条件分组统计
users.stream()
.collect(Collectors.groupingBy(
u -> u.getAge() >= 18 ? "成年" : "未成年",
Collectors.counting()));
// 找出重复元素
Set<String> seen = new HashSet<>();
List<String> duplicates = list.stream()
.filter(s -> !seen.add(s))
.distinct()
.collect(Collectors.toList());
// 扁平化 + 去重 + 排序
List<String> tags = articles.stream()
.flatMap(a -> a.getTags().stream())
.distinct()
.sorted()
.collect(Collectors.toList());