Java 8 引入的 Stream API 是处理集合数据的革命性工具,它通过声明式编程(告诉计算机“做什么”而非“怎么做”)简化了集合操作,支持链式调用、并行处理,并能显著提升代码可读性和开发效率。本教程将从基础概念出发,逐步带你掌握 Stream 的核心用法。
![图片[1]_Java Stream 入门级教程:从基础到实战_知途无界](https://zhituwujie.com/wp-content/uploads/2025/11/d2b5ca33bd20251101101043.png)
一、Stream 是什么?
1. 核心概念
- 不是数据结构:Stream 本身不存储数据,而是对数据源(如集合、数组)的抽象操作流水线。
- 惰性执行:中间操作(如
filter、map)不会立即执行,只有遇到终止操作(如collect、forEach)时才会触发整个流水线处理。 - 不可复用:Stream 一旦被终止操作消费,就不能再次使用。
2. 数据源
Stream 的数据来源通常是集合(如 List、Set)、数组,或通过 Stream.of() 直接创建。
二、Stream 的基本操作流程
Stream 操作分为两大类:
- 中间操作(返回新的 Stream,可链式调用)
如:filter()、map()、sorted()、distinct()。 - 终止操作(触发实际计算,返回结果或副作用)
如:collect()、forEach()、count()、reduce()。
流程示例:数据源 → 中间操作1 → 中间操作2 → ... → 终止操作 → 结果
三、Stream 的创建方式
1. 从集合创建(最常用)
List<String> list = Arrays.asList("apple", "banana", "orange");
Stream<String> stream = list.stream(); // 创建普通顺序流
Stream<String> parallelStream = list.parallelStream(); // 创建并行流(多线程处理)
2. 从数组创建
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
3. 直接创建(静态方法)
// 创建包含指定元素的流
Stream<String> stream1 = Stream.of("hello", "world");
// 创建空流
Stream<Object> emptyStream = Stream.empty();
// 通过生成器创建无限流(需结合 limit 限制数量)
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2); // 生成 0, 2, 4, 6...
Stream<Integer> limitedStream = infiniteStream.limit(5); // 限制前5个:0, 2, 4, 6, 8
四、常用中间操作
1. 过滤:filter(Predicate)
筛选满足条件的元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0) // 保留偶数
.collect(Collectors.toList()); // 终止操作:转为List
// 结果:[2, 4]
2. 映射:map(Function)
将每个元素转换为另一种形式。
List<String> words = Arrays.asList("java", "stream", "api");
List<Integer> wordLengths = words.stream()
.map(String::length) // 转换为字符串长度
.collect(Collectors.toList());
// 结果:[4, 6, 3]
3. 去重:distinct()
移除重复元素(依赖元素的 equals() 和 hashCode())。
List<Integer> nums = Arrays.asList(1, 2, 2, 3, 3);
List<Integer> uniqueNums = nums.stream()
.distinct()
.collect(Collectors.toList());
// 结果:[1, 2, 3]
4. 排序:sorted()
自然排序或自定义排序。
List<String> fruits = Arrays.asList("banana", "apple", "orange");
// 自然排序(按字母顺序)
List<String> sortedFruits = fruits.stream()
.sorted()
.collect(Collectors.toList());
// 结果:[apple, banana, orange]
// 自定义排序(按字符串长度降序)
List<String> customSorted = fruits.stream()
.sorted((s1, s2) -> s2.length() - s1.length())
.collect(Collectors.toList());
// 结果:[banana, orange, apple]
5. 截取:limit() 和 skip()
limit(n):保留前 n 个元素。skip(n):跳过前 n 个元素。
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> firstThree = nums.stream()
.limit(3) // 取前3个
.collect(Collectors.toList());
// 结果:[1, 2, 3]
List<Integer> afterSkipTwo = nums.stream()
.skip(2) // 跳过前2个
.collect(Collectors.toList());
// 结果:[3, 4, 5]
五、常用终止操作
1. 收集结果:collect(Collector)
将 Stream 元素转换为集合或其他格式(最常用)。
// 转为 List
List<String> list = stream.collect(Collectors.toList());
// 转为 Set(自动去重)
Set<String> set = stream.collect(Collectors.toSet());
// 转为 Map(需指定键和值的生成规则)
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Map<String, Integer> nameLengths = names.stream()
.collect(Collectors.toMap(
name -> name, // 键:原字符串
name -> name.length() // 值:字符串长度
));
// 结果:{Alice=5, Bob=3, Charlie=7}
2. 遍历:forEach(Consumer)
对每个元素执行操作(无返回值)。
List<String> fruits = Arrays.asList("apple", "banana");
fruits.stream().forEach(System.out::println); // 依次打印:apple, banana
3. 统计:count()
返回元素总数。
long count = Arrays.asList(1, 2, 3).stream().count();
// 结果:3
4. 匹配:anyMatch() / allMatch() / noneMatch()
anyMatch(predicate):是否有任意元素满足条件。allMatch(predicate):是否所有元素都满足条件。noneMatch(predicate):是否所有元素都不满足条件。
List<Integer> nums = Arrays.asList(2, 4, 6);
boolean hasEven = nums.stream().anyMatch(n -> n % 2 == 0); // 是否有偶数?true
boolean allEven = nums.stream().allMatch(n -> n % 2 == 0); // 是否全是偶数?true
5. 查找:findFirst() / findAny()
findFirst():返回第一个元素(顺序流中确定,并行流中可能不确定)。findAny():返回任意一个元素(并行流中更高效)。
Optional<Integer> first = Arrays.asList(1, 2, 3).stream()
.findFirst();
first.ifPresent(System.out::println); // 输出:1
6. 聚合:reduce(BinaryOperator)
将元素合并为一个结果(如求和、拼接字符串)。
// 求和
List<Integer> nums = Arrays.asList(1, 2, 3);
int sum = nums.stream().reduce(0, (a, b) -> a + b); // 0 是初始值
// 结果:6
// 拼接字符串
List<String> words = Arrays.asList("Hello", "World");
String combined = words.stream().reduce("", (a, b) -> a + " " + b).trim();
// 结果:"Hello World"
六、实战示例
示例1:统计单词长度
List<String> sentences = Arrays.asList("Java is fun", "Stream API rocks");
List<Integer> wordLengths = sentences.stream()
.flatMap(sentence -> Arrays.stream(sentence.split(" "))) // 拆分单词并扁平化为一个流
.map(String::length)
.collect(Collectors.toList());
// 结果:[4, 2, 3, 6, 3, 5](Java, is, fun, Stream, API, rocks 的长度)
示例2:分组统计
List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");
// 按首字母分组
Map<Character, List<String>> grouped = fruits.stream()
.collect(Collectors.groupingBy(s -> s.charAt(0)));
// 结果:{a=[apple], b=[banana], c=[cherry], d=[date]}
七、注意事项
- 避免副作用:中间操作应尽量无副作用(如不修改外部变量),终止操作如
forEach需谨慎使用(推荐用collect替代复杂逻辑)。 - 并行流慎用:
parallelStream()虽能提升性能,但需确保操作是线程安全的(如共享变量加锁)。 - 短路操作:部分终止操作(如
findFirst、anyMatch)在满足条件时会提前终止,提升效率。
通过本教程,你已经掌握了 Stream 的基础用法!接下来可以尝试在项目中用 Stream 替换传统的 for 循环,体验更简洁高效的代码风格。 🚀
© 版权声明
文中内容均来源于公开资料,受限于信息的时效性和复杂性,可能存在误差或遗漏。我们已尽力确保内容的准确性,但对于因信息变更或错误导致的任何后果,本站不承担任何责任。如需引用本文内容,请注明出处并尊重原作者的版权。
THE END

























暂无评论内容