Java 8 引入的 Stream 流是处理集合的革命性特性,它以声明式编程风格对集合元素进行流水线式处理,简化了数据过滤、转换、聚合等操作。与传统的 for 循环相比,Stream 流更注重 “做什么” 而非 “怎么做”,能显著减少代码量并提升可读性。Stream 流基于集合创建,支持链式调用,适用于各种复杂的集合处理场景。掌握 Stream 流的使用方法和常用操作,是编写高效 Java 代码的重要技能。
一、Stream 流操作集合的基本流程
Stream 流操作集合通常分为三个步骤:创建流→中间操作→终止操作,形成一个完整的处理流水线。
(一)创建流:从集合获取流对象
所有 Collection 接口的实现类(如 List、Set)都可通过 stream() 方法创建顺序流,或 parallelStream() 方法创建并行流(多线程处理)。
示例:
import java.util.Arrays;import java.util.List;import java.util.stream.Stream;public class StreamDemo { public static void main(String[] args) { List<String> fruits = Arrays.asList("apple", "banana", "orange", "grape"); // 创建顺序流 Stream<String> stream = fruits.stream(); // 创建并行流(适合大数据量处理) Stream<String> parallelStream = fruits.parallelStream(); }}
(二)中间操作:处理数据(链式调用)
中间操作用于对元素进行过滤、转换、排序等处理,返回新的流对象,支持链式调用。常见中间操作包括 filter()、map()、sorted() 等。
(三)终止操作:生成结果(触发执行)
终止操作会触发整个流的处理流程,返回一个非流结果(如集合、数值、布尔值等)。一旦执行终止操作,流就会关闭,无法再次使用。常见终止操作包括 collect()、forEach()、count() 等。
完整示例:筛选长度大于 5 的水果并打印
List<String> fruits = Arrays.asList("apple", "banana", "orange", "grape");// 流程:创建流 → 过滤 → 打印(终止操作)fruits.stream() .filter(fruit -> fruit.length() > 5) // 中间操作:筛选长度>5的元素 .forEach(System.out::println); // 终止操作:打印结果// 输出:banana、orange
二、常用的 Stream 流方法
(一)中间操作方法
filter(Predicate<T> predicate):根据条件过滤元素,保留满足条件的元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);numbers.stream() .filter(n -> n % 2 == 0) // 筛选偶数 .forEach(System.out::println); // 输出:2、4、6
map(Function<T, R> mapper):将元素转换为另一种类型(如字符串转长度、对象转属性)。
List<String> words = Arrays.asList("hello", "world");words.stream() .map(String::length) // 转换为字符串长度 .forEach(System.out::println); // 输出:5、5
sorted() / sorted(Comparator<T> comparator):对元素排序,默认按自然顺序,也可自定义比较器。
List<Integer> nums = Arrays.asList(3, 1, 4, 2);// 自然排序(升序)nums.stream().sorted().forEach(System.out::println); // 1、2、3、4// 自定义排序(降序)nums.stream() .sorted((a, b) -> b - a) .forEach(System.out::println); // 4、3、2、1
distinct():去除重复元素(依赖 equals() 方法判断)。
List<Integer> duplicates = Arrays.asList(1, 2, 2, 3, 3, 3);duplicates.stream().distinct().forEach(System.out::println); // 1、2、3
limit(long maxSize):截取前 maxSize 个元素。
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);nums.stream().limit(3).forEach(System.out::println); // 1、2、3
(二)终止操作方法
collect(Collector<T, A, R> collector):将流转换为集合(如 List、Set)或其他类型。
import java.util.List;import java.util.stream.Collectors;List<String> fruits = Arrays.asList("apple", "banana", "orange");// 转换为ListList<String> filteredList = fruits.stream() .filter(f -> f.startsWith("a")) .collect(Collectors.toList()); // [apple]
forEach(Consumer<T> action):遍历元素并执行操作(如打印、修改)。
List<String> languages = Arrays.asList("Java", "Python");languages.stream().forEach lang -> System.out.println("Language: " + lang));
count():返回元素个数(long 类型)。
List<Integer> nums = Arrays.asList(1, 2, 3, 4);long count = nums.stream().filter(n -> n > 2).count(); // 结果:2
findFirst() / findAny():返回第一个元素或任意一个元素(用于并行流),返回 Optional<T> 类型。
List<String> fruits = Arrays.asList("apple", "banana");String first = fruits.stream().findFirst().orElse("默认值"); // apple
reduce(T identity, BinaryOperator<T> accumulator):聚合元素(如求和、求积)。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);// 求和(初始值0,累加操作)int sum = numbers.stream().reduce(0, Integer::sum); // 1+2+3+4=10
三、Stream 流的优势与注意事项
(一)优势
代码简洁:链式调用替代冗长的循环和条件判断,可读性更高。
并行处理:通过 parallelStream() 轻松实现多线程处理,提升大数据量处理效率。
函数式编程:结合 Lambda 表达式,专注业务逻辑而非迭代细节。
(二)注意事项
流的一次性:流只能执行一次终止操作,再次使用会抛出 IllegalStateException。
无状态性:中间操作不应修改外部变量(避免副作用),推荐使用不可变数据。
性能考量:小数据量场景下,Stream 流的性能与传统循环接近;大数据量或复杂操作时,并行流优势更明显。
Java Stream 流通过 “创建流→中间操作→终止操作” 的流程,为集合处理提供了高效且优雅的解决方案。常用的中间操作(如 filter、map)和终止操作(如 collect、forEach)能覆盖大多数业务场景,结合 Lambda 表达式可大幅简化代码。合理使用 Stream 流,不仅能提升开发效率,还能让代码更易维护和扩展,是现代 Java 开发的必备技能。