Java8+的新特性——Stream API(流操作) 详细讲解

一、Stream 核心概念

Stream 是 Java 8 引入的用于处理集合数据的抽象 API,支持 函数式编程链式操作

核心特点:

  • 无存储:不存储数据,仅通过操作管道处理数据源(集合、数组等)。

  • 惰性求值:中间操作(Intermediate Operations)延迟执行,终端操作(Terminal Operations)触发实际计算。

  • 不可重用:流一旦被消费(终端操作执行),不可重复使用。

  • 并行处理:可通过 parallel() 方法启用并行流,利用多核CPU加速计算。

二、Stream 生命周期

  1. 创建流:从数据源(集合、数组等)生成流。

  2. 中间操作:对流进行过滤、转换等操作,返回新流。

  3. 终端操作:触发实际计算,生成最终结果或副作用。

三、Stream 核心 API 详解

3.1. 创建流的 API

方法说明
Collection.stream()从集合创建顺序流
Collection.parallelStream()从集合创建并行流
Arrays.stream(T[] array)从数组创建流
Stream.of(T... values)从一组元素创建流(支持可变参数)
Stream.iterate(T seed, UnaryOperator<T> f)生成无限流(迭代生成元素,如 0, 2, 4, 6...
Stream.generate(Supplier<T> s)生成无限流(通过 Supplier 不断生成元素,如随机数)
Stream.empty()创建空流
Stream.concat(Stream a, Stream b)合并两个流
IntStream.range(int startInclusive, int endExclusive)生成整数范围流(不包含结束值)
Files.lines(Path path)从文件按行生成流
        // 从集合创建顺序流 打印集合中的元素
        List<String> list = Arrays.asList("a", "b", "c");
        Stream<String> stream1 = list.stream();
        stream1.forEach(System.out::println);

        // 创建并行流 输出所有元素 parallelStream主要用于利用cpu的多个核心。通常,任何Java代码都有一个处理流,按顺序执行(向上面一样)。
        // 然而,通过使用并行流,我们可以将代码分成多个流,这些流在不同的内核上并行执行,最终的结果是各个结果的组合。然而,处理的顺序不在我们的控制之下。
        //因此,建议在以下情况下使用并行流:无论执行顺序如何,结果不受影响,一个元素的状态不影响另一个元素,并且数据源也不受影响。
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Stream<Integer> parallelStream = numbers.parallelStream();
        parallelStream.forEach(n -> System.out.println(Thread.currentThread().getName() + ": " + n));

        //从数组创建流
        String[] array = {"Java", "Python", "C++"};
        Stream<String> stream = Arrays.stream(array);  // 从数组创建流
        stream.map(String::toUpperCase).forEach(System.out::println);  // 转为大写输出

        //从一组元素直接创建流
        Stream<String> directStreams = Stream.of("One", "Two", "Three");  // 直接传入元素 接收T... 即多个类型为T的元素
        directStreams.filter(s -> s.length() > 3).forEach(System.out::println);  // 输出长度>3的字符串

        // 生成 0, 2, 4, 6, 8(限制5个元素)
        //iterator参数
        //Params:
        //T seed – 初始元素
        //UnaryOperator<T> f –  UnaryOperator<T> 是一个泛型接口 继承自 Function<T, T>是一种特殊的 Function,其输入和输出类型相同。
        Stream.iterate(0, n -> n + 2)
                .limit(5)
                .forEach(System.out::println);



        //Stream.generate():生成无限流(Supplier)
        // 生成3个随机数
        Stream.generate(new Random()::nextDouble)
                .limit(3)
                .forEach(System.out::println);

        //Stream.empty():创建空流
        //顾名思义,就是空的,啥也没有就像空数组,但是这并不是null。流操作过滤、映射等最后没有符合条件的就会返回空流,保证不会出现null操作

        //Stream.concat():合并两个流
        Stream<String> strStream1 = Stream.of("A", "B");
        Stream<String> strStream2 = Stream.of("X", "Y");
        Stream<String> mergedStream = Stream.concat(strStream1, strStream2);  // 合并流
        mergedStream.forEach(System.out::println);  // 输出 A, B, X, Y

        //IntStream.range():生成整数范围流
        // 生成 1~4(不包含5)
        IntStream.range(1, 5)
                .forEach(n -> System.out.print(n + " "));  // 输出 1 2 3 4

        //Files.lines():从文件按行生成流
        // 读取文件内容(需处理 IOException)
        try (Stream<String> lines = Files.lines(Paths.get("demo.txt"))) {
            lines.filter(line -> line.contains("Java"))
                    .forEach(System.out::println);
        } catch (Exception e) {
            e.printStackTrace();
        }

 

  • 集合与数组:优先使用 Collection.stream()Arrays.stream()

  • 动态元素:使用 Stream.of()Stream.iterate()/Stream.generate()

  • 合并与空流Stream.concat()Stream.empty() 用于特殊场景。

  • 文件处理Files.lines() 适合逐行处理大文件(自动关闭资源)。

3.2 重点

重点:在Java中实现了Collection接口或者是其子接口的对象均能调用.stream()方法,能够将对象中的元素进行流式处理。因为stream() 方法是 java.util.Collection 接口中的默认方法。这一部分才是我们常用的。正常的List、Set、Queue、Deque接口均继承自Collection接口,所以可以直接使用stream()函数,Map<K,V>接口是没有继承Collection接口的,这是不能直接使用流操作的。Array数组虽然没有直接实现Collection接口,但是可以通过上面的Arrays.stream()创建流。下面是Collection接口源码截图。

3.2 中间操作(Intermediate Operations)

中间操作返回新流,支持链式调用,不触发实际计算

过滤与切片

方法说明
filter(Predicate<T> predicate)过滤元素,保留满足条件的元素
distinct()去重(依赖 equals()hashCode()
limit(long maxSize)截取前 N 个元素
skip(long n)跳过前 N 个元素
takeWhile(Predicate<T> predicate) (Java 9+)取元素直到条件不满足为止(类似 break
dropWhile(Predicate<T> predicate) (Java 9+)丢弃元素直到条件不满足为止
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
numbers.stream()
       .filter(n -> n % 2 == 0)  // 保留偶数:2,4,6
       .distinct()                // 去重(此处无重复)
       .limit(2)                 // 取前2个:2,4
       .skip(1)                  // 跳过1个:4
       .forEach(System.out::println);

映射与转换

方法说明
map(Function<T, R> mapper)将元素转换为另一种类型(1:1 转换)
flatMap(Function<T, Stream<R>> mapper)将每个元素转换为流后合并(1:N 转换)
mapToInt(ToIntFunction<T> mapper)转换为 IntStream(原始类型流,避免装箱开销)
mapToLong(ToLongFunction<T> mapper)转换为 LongStream
mapToDouble(ToDoubleFunction<T> mapper)转换为 DoubleStream
        // map:将字符串转为大写
        List<String> words = Arrays.asList("apple", "banana");
        words.stream()
                .map(String::toUpperCase) // APPLE, BANANA
                .forEach(System.out::println);
        
        // flatMap:拆分字符串
        List<String> lines = Arrays.asList("Hello World", "Java Stream");
        lines.stream()
                .flatMap(line -> Arrays.stream(line.split(" "))) // Hello, World, Java, Stream
                .forEach(System.out::println);

排序与去重

方法说明
sorted()自然排序(元素需实现 Comparable接口
sorted(Comparator<T> comparator)自定义排序
        List<String> names = Arrays.asList("Tom", "我Alice", "Bob");
        names.stream()
                // 自然排序的逆序:Tom, Bob, Alice 自然排序是由对象自身的 compareTo 方法施加的排序。
                //String对象的自然排序是按照字符的Unicode值升序排列的
                .sorted(Comparator.reverseOrder())
                .forEach(System.out::println);

其他中间操作

方法说明
peek(Consumer<T> action)查看流中元素(调试用,不影响流)
Stream.of("one", "two", "three")
      .filter(s -> s.length() > 3)
      .peek(s -> System.out.println("Filtered: " + s)) // 输出符合条件的元素
      .map(String::toUpperCase)
      .forEach(System.out::println);

3.2 终端操作(Terminal Operations)

终端操作触发流的实际计算,生成结果或副作用,执行后流被关闭

遍历与匹配

方法说明
forEach(Consumer<T> action)遍历每个元素(无顺序保证,并行流中无序)
forEachOrdered(Consumer<T> action)按流顺序遍历元素(保证顺序,并行流中性能较低)
anyMatch(Predicate<T> predicate)是否至少有一个元素匹配条件(短路操作)
allMatch(Predicate<T> predicate)是否所有元素匹配条件(短路操作)
noneMatch(Predicate<T> predicate)是否没有元素匹配条件(短路操作)
List<Integer> nums = Arrays.asList(1, 3, 5, 7, 9);
boolean hasEven = nums.stream().anyMatch(n -> n % 2 == 0); // false
boolean allLess10 = nums.stream().allMatch(n -> n < 10);   // true

归约与聚合

方法说明
reduce(T identity, BinaryOperator<T> accumulator)归约操作(初始值 + 累积函数)
reduce(BinaryOperator<T> accumulator)归约操作(无初始值,返回 Optional<T>
count()返回元素个数
max(Comparator<T> comparator)返回最大元素(Optional<T>
min(Comparator<T> comparator)返回最小元素(Optional<T>
// 求和
int sum = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum); // 10

// 找最大值 //不理解Optional<T>接口的,可以看参考中的第二篇文章
Optional<Integer> max = Stream.of(5, 3, 8).max(Integer::compare);
max.ifPresent(System.out::println); // 8

收集结果

方法说明
collect(Collector<T, A, R> collector)使用收集器将流转换为集合或其他结构(最强大的终端操作,十分常用)

 常用 Collectors 工具类方法

        Stream<String> stream = Stream.of("Apple", "Banana", "Cherry", "Apple");
        // 转换为 List(允许重复)
        List<String> list = stream.collect(Collectors.toList());
        System.out.println("List: " + list); // [Apple, Banana, Cherry, Apple]


        // 重新创建流(流只能消费一次)
        stream = Stream.of("Apple", "Banana", "Cherry", "Apple");
        // 转换为 Set(自动去重)
        Set<String> set = stream.collect(Collectors.toSet());
        System.out.println("Set: " + set); // [Apple, Cherry, Banana]


        stream = Stream.of("Apple", "Banana", "Cherry");
        // 转换为 Map:键=字符串,值=长度
        Map<String, Integer> map = stream.collect(
                Collectors.toMap(
                        s -> s,          // 键生成规则
                        String::length   // 值生成规则
                )
        );
        System.out.println("Map: " + map); // {Apple=5, Banana=6, Cherry=6}


        stream = Stream.of("Apple", "Banana", "Cherry", "Date", "Elderberry");
        // 按字符串长度分组
        Map<Integer, List<String>> groupByLength = stream.collect(
                Collectors.groupingBy(String::length)
        );
        System.out.println("Group by length: " + groupByLength);
        // 输出: {4=[Date], 5=[Apple], 6=[Banana, Cherry], 10=[Elderberry]}



        stream = Stream.of("Apple", "Banana", "Cherry", "Date", "Fig");
        // 按长度是否大于5分区
        Map<Boolean, List<String>> partition = stream.collect(
                Collectors.partitioningBy(s -> s.length() > 5)
        );
        System.out.println("Partition: " + partition);
        // 输出: {false=[Apple, Date, Fig], true=[Banana, Cherry]}


        stream = Stream.of("Apple", "Banana", "Cherry");
        // 用逗号和空格连接字符串
        String joined = stream.collect(Collectors.joining(", "));
        System.out.println("Joined: " + joined); // Apple, Banana, Cherry


        stream = Stream.of("Apple", "Banana", "Cherry", "Date");
        // 统计字符串长度的汇总信息
        IntSummaryStatistics stats = stream.collect(
                Collectors.summarizingInt(String::length)
        );
        System.out.println("平均值: " + stats.getAverage()); // 5.25
        System.out.println("总数: " + stats.getCount());    // 4
        System.out.println("最大值: " + stats.getMax());     // 6
        System.out.println("最小值: " + stats.getMin());     // 4
        System.out.println("总和: " + stats.getSum());       // 21

其他终端操作

方法说明
findFirst()返回第一个元素(Optional<T>
findAny()返回任意元素(并行流中效率更高)
toArray()将流转换为数组
Optional<String> first = Stream.of("a", "b", "c").findFirst();
String[] array = Stream.of("a", "b", "c").toArray(String[]::new);

4. 原始类型特化流

为减少装箱开销,Java 提供了针对原始类型的流:

  • IntStream:处理 int 类型数据。

  • LongStream:处理 long 类型数据。

  • DoubleStream:处理 double 类型数据。

IntStream.range(1, 5).forEach(System.out::println); // 1,2,3,4
DoubleStream.of(1.1, 2.2).sum(); // 3.3

5. 并行流与性能优化

通过 parallel() 将顺序流转为并行流:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream()
                 .mapToInt(n -> n * 2)
                 .sum();

注意事项

  • 线程安全:确保操作无状态且不修改共享数据。

  • 避免有状态操作:如 sorted() 在并行流中性能可能下降。

  • 评估开销:数据量小时可能得不偿失。

6. Stream API 最佳实践

  1. 优先使用方法引用和 Lambda:简化代码。

  2. 复用 Collectors:利用预定义的收集器减少代码量。

  3. 谨慎使用并行流:仅在数据量大且无状态时使用。

  4. 组合操作:链式调用保持简洁。

Java Stream API 通过声明式编程极大简化了集合操作,核心方法包括:

  • 创建流stream(), Arrays.stream(), Stream.of()

  • 中间操作filter, map, flatMap, sorted, distinct

  • 终端操作collect, forEach, reduce, count

结合 Collectors 工具类,可以实现复杂的数据转换和聚合。合理使用 Stream API 能提升代码简洁性和可维护性,但在性能敏感场景需谨慎选择并行流。

7.参考

Java进阶-Java Stream API详解与使用-CSDN博客 

JAVA 中的Optional类理解、学习与使用_java的optional类-CSDN博客

......

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新停浊酒杯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值