Java Stream 流基本概念和使用方式详解

 更新时间:2025年09月02日 09:43:02   作者:MadeInSQL  
Stream是Java 8 引入的全新API,它基于函数式编程思想,为集合操作提供了一种声明式的处理方式,本文给大家介绍Java Stream流基本概念和核心特性,感兴趣的朋友一起看看吧

一、Stream 基础概念与核心特性

1.1 什么是 Stream 流

Stream 是 Java 8 引入的全新API,它基于函数式编程思想,为集合操作提供了一种声明式的处理方式。与传统的命令式集合操作不同,Stream API 使得数据处理更加高效且易于并行化。

核心特点详解:

  • 惰性求值(Lazy Evaluation):
    • 中间操作(如 filter、map)不会立即执行
    • 只有遇到终止操作(如 collect、forEach)时才会触发计算
    • 示例:list.stream().filter(x -> x > 10).count()中,filter 操作在 count 被调用时才执行
  • 不可复用(Single Use):
    • 每个 Stream 管道(pipeline)只能被消费一次
    • 重复使用会抛出 IllegalStateException
    • 解决方案:每次需要时重新创建 Stream
  • 内部迭代(Internal Iteration):
    • 不需要显式编写 for/while 循环
    • 迭代过程由 Stream API 内部处理
    • 对比:传统 for 循环是外部迭代
  • 函数式风格(Functional Style):
    • 支持 Lambda 表达式(如 x -> x*2)
    • 支持方法引用(如 String::length)
    • 无副作用:理想情况下不修改外部状态

典型使用场景:

  • 数据筛选过滤(filter)
  • 数据转换映射(map)
  • 聚合计算(reduce、collect)
  • 并行数据处理(parallelStream)

示例代码:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 传统方式
for(String name : names) {
    if(name.startsWith("A")) {
        System.out.println(name.toUpperCase());
    }
}
// Stream方式
names.stream()
     .filter(name -> name.startsWith("A"))
     .map(String::toUpperCase)
     .forEach(System.out::println);

1.2 Stream 与集合的区别

特性集合(Collection)Stream(流)
存储存储实际数据不存储数据
操作方式外部迭代(foreach循环)内部迭代
数据处理立即执行延迟执行
可重用性可多次遍历只能遍历一次
并行能力需要手动实现内置并行支持

二、Stream 创建方式详解

2.1 从集合创建

// 从List创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream1 = list.stream();
// 从Set创建
Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3));
Stream<Integer> stream2 = set.stream();

2.2 从数组创建

String[] array = {"a", "b", "c"};
Stream<String> stream3 = Arrays.stream(array);
// 指定范围
Stream<String> stream4 = Arrays.stream(array, 1, 3); // "b", "c"

2.3 使用静态工厂方法

// 使用Stream.of()
Stream<String> stream5 = Stream.of("a", "b", "c");
// 创建空流
Stream<String> emptyStream = Stream.empty();
// 创建无限流
Stream<Integer> infiniteStream1 = Stream.iterate(0, n -> n + 2); // 0,2,4,6...
Stream<Double> infiniteStream2 = Stream.generate(Math::random); // 随机数流

2.4 其他创建方式

// 从文件创建
try (Stream<String> lines = Files.lines(Paths.get("data.txt"))) {
    lines.forEach(System.out::println);
}
// 从字符串创建
IntStream charStream = "Hello".chars();

三、Stream 核心操作全解析

3.1 中间操作(Intermediate Operations)

过滤操作

// filter() - 筛选偶数
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evens = numbers.stream()
                           .filter(n -> n % 2 == 0)
                           .collect(Collectors.toList()); // [2, 4]
// distinct() - 去重
List<String> names = Arrays.asList("John", "John", "Mary");
List<String> uniqueNames = names.stream()
                              .distinct()
                              .collect(Collectors.toList()); // ["John", "Mary"]

映射操作

// map() - 字符串转大写
List<String> upperNames = names.stream()
                             .map(String::toUpperCase)
                             .collect(Collectors.toList());
// flatMap() - 扁平化处理
List<List<String>> nestedList = Arrays.asList(
    Arrays.asList("a", "b"),
    Arrays.asList("c", "d")
);
List<String> flatList = nestedList.stream()
                                .flatMap(Collection::stream)
                                .collect(Collectors.toList()); // ["a", "b", "c", "d"]

限制与跳过

// limit() - 限制数量
List<Integer> limited = numbers.stream()
                             .limit(3)
                             .collect(Collectors.toList()); // [1, 2, 3]
// skip() - 跳过元素
List<Integer> skipped = numbers.stream()
                            .skip(2)
                            .collect(Collectors.toList()); // [3, 4, 5]

排序操作

// sorted() - 自然排序
List<String> sortedNames = names.stream()
                              .sorted()
                              .collect(Collectors.toList());
// sorted(Comparator) - 自定义排序
List<Person> people = Arrays.asList(
    new Person("John", 25),
    new Person("Mary", 20)
);
List<Person> sortedByAge = people.stream()
                               .sorted(Comparator.comparing(Person::getAge))
                               .collect(Collectors.toList());

3.2 终端操作(Terminal Operations)

匹配与查找

// anyMatch() - 是否有元素匹配
boolean hasEven = numbers.stream()
                       .anyMatch(n -> n % 2 == 0); // true
// findFirst() - 获取第一个元素
Optional<Integer> first = numbers.stream()
                               .findFirst(); // Optional[1]

聚合操作

// count() - 计数
long count = numbers.stream().count(); // 5
// max()/min() - 最大最小值
Optional<Integer> max = numbers.stream()
                             .max(Integer::compare); // Optional[5]
// reduce() - 归约操作
Optional<Integer> sum = numbers.stream()
                             .reduce(Integer::sum); // Optional[15]

收集操作

// collect() - 转换为集合
Set<Integer> numberSet = numbers.stream()
                              .collect(Collectors.toSet());
// joining() - 字符串连接
String joined = names.stream()
                   .collect(Collectors.joining(", ")); // "John, John, Mary"
// groupingBy() - 分组
Map<Integer, List<Person>> peopleByAge = people.stream()
                                             .collect(Collectors.groupingBy(Person::getAge));

遍历操作

// forEach() - 遍历消费
numbers.stream().forEach(System.out::println);
// forEachOrdered() - 保证顺序
numbers.parallelStream().forEachOrdered(System.out::println);

四、并行流与性能优化

4.1 并行流基础

// 创建并行流
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 方式1:从集合创建
Stream<Integer> parallelStream1 = numbers.parallelStream();
// 方式2:将顺序流转并行
Stream<Integer> parallelStream2 = numbers.stream().parallel();
// 示例:并行处理
long count = numbers.parallelStream()
                  .filter(n -> n % 2 == 0)
                  .count();

4.2 性能考量

适合使用并行流的场景

  • 数据量较大(通常超过1万元素)
  • 单个元素处理耗时较长
  • 操作是无状态的(不依赖之前处理的结果)
  • 操作不要求顺序

注意事项

  • 并行化本身有开销,小数据集可能更慢
  • 确保线程安全,避免共享可变状态
  • 考虑使用unordered()去除顺序约束提升性能

4.3 自定义线程池

// 使用自定义ForkJoinPool
ForkJoinPool customPool = new ForkJoinPool(4);
long result = customPool.submit(() -> 
    numbers.parallelStream()
         .filter(n -> n % 2 == 0)
         .count()
).get();

五、实战应用案例

5.1 数据处理案例

// 案例1:统计单词频率
List<String> words = Arrays.asList("hello", "world", "hello", "java");
Map<String, Long> wordCounts = words.stream()
                                  .collect(Collectors.groupingBy(
                                      Function.identity(),
                                      Collectors.counting()
                                  ));
// 案例2:多级分组
List<Employee> employees = ...;
Map<String, Map<String, List<Employee>>> byDeptAndCity = employees.stream()
    .collect(Collectors.groupingBy(Employee::getDepartment,
             Collectors.groupingBy(Employee::getCity)));

5.2 文件处理案例

// 读取文件并处理
try (Stream<String> lines = Files.lines(Paths.get("data.txt"))) {
    List<String> filteredLines = lines
        .filter(line -> !line.startsWith("#")) // 过滤注释行
        .map(String::trim)                     // 去除空格
        .collect(Collectors.toList());
}
// 查找文件中最长的行
Optional<String> longest = Files.lines(Paths.get("data.txt"))
    .max(Comparator.comparing(String::length));

5.3 数据库查询结果处理

// 使用Stream处理JDBC结果
List<Person> persons = new ArrayList<>();
try (Connection conn = DriverManager.getConnection(url, user, pass);
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery("SELECT * FROM persons")) {
    Stream<ResultSet> stream = StreamSupport.stream(
        new Spliterators.AbstractSpliterator<ResultSet>(
            Long.MAX_VALUE, Spliterator.ORDERED) {
            public boolean tryAdvance(Consumer<? super ResultSet> action) {
                try {
                    if (!rs.next()) return false;
                    action.accept(rs);
                    return true;
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
        }, false);
    persons = stream.map(rs -> new Person(rs.getString("name"), rs.getInt("age")))
                   .collect(Collectors.toList());
}

六、常见问题与最佳实践

6.1 常见陷阱

  1. 重复使用Stream

    Stream<String> stream = Stream.of("a", "b", "c");
    stream.forEach(System.out::println);
    stream.forEach(System.out::println); // 抛出IllegalStateException
    
  2. 修改源集合

    List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
    Stream<String> stream = list.stream();
    list.add("d"); // 可能导致ConcurrentModificationException
    
  3. 无限流未限制

    Stream.iterate(0, i -> i + 1)
        .forEach(System.out::println); // 无限循环
    

6.2 性能优化建议

  1. 优先使用基本类型流

    // 使用IntStream代替Stream<Integer>
    IntStream.range(0, 100).sum();
    
  2. 短路操作优化

    // 使用anyMatch代替filter+findFirst
    boolean hasNegative = numbers.stream().anyMatch(n -> n < 0);
    
  3. 合并中间操作

    // 合并多个filter
    list.stream().filter(s -> s != null && s.length() > 3);
    

6.3 调试技巧

  1. 使用peek()调试

    List<String> result = list.stream()
        .peek(System.out::println)  // 调试输出
        .filter(s -> s.length() > 3)
        .peek(System.out::println)  // 调试输出
        .collect(Collectors.toList());
    
  2. 转换为并行流查找问题

    list.stream()
        .parallel()
        .map(s -> {throw new RuntimeException();}) // 更容易暴露线程安全问题
        .collect(Collectors.toList());
    

七、高级特性与未来展望

7.1 Java 9+ 增强特性

// takeWhile/dropWhile (Java 9+)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 3, 2, 1);
List<Integer> taken = numbers.stream()
                           .takeWhile(n -> n < 4) // [1, 2, 3]
                           .collect(Collectors.toList());
// ofNullable (Java 9+)
Stream<String> stream = Stream.ofNullable(System.getProperty("key"));
// iterate增强 (Java 9+)
Stream.iterate(0, n -> n < 10, n -> n + 1) // 类似for循环
      .forEach(System.out::println);

7.2 响应式编程结合

// 与RxJava/Reactor结合示例
Flux.fromStream(Stream.generate(() -> "data"))
    .delayElements(Duration.ofMillis(100))
    .take(10)
    .subscribe(System.out::println);

7.3 未来发展方向

  1. 值类型流:对基本类型更高效的支持
  2. 模式匹配:增强流处理表达能力
  3. 更强大的收集器:提供更多内置收集操作

到此这篇关于Java Stream 流详解的文章就介绍到这了,更多相关Java Stream 流内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

到此这篇关于Java Stream 流基本概念和使用方式详解的文章就介绍到这了,更多相关Java Stream 流内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 关于java中多个JDK和切换版本介绍

    关于java中多个JDK和切换版本介绍

    大家好,本篇文章主要讲的是关于java中多个JDK和切换版本介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2022-01-01
  • 在SpringBoot中配置和使用多个数据源方式

    在SpringBoot中配置和使用多个数据源方式

    本文介绍如何在Spring Boot中配置多数据源,需通过配置类声明不同数据源bean,使用@ConfigurationProperties映射属性,并针对SpringDataJDBC和JPA分别配置JdbcTemplate与EntityManagerFactory,注意拆分包及@Primary注解的使用,同时可配置Hikari连接池
    2025-08-08
  • java的五种数据类型解析

    java的五种数据类型解析

    下面小编就为大家带来一篇java的五种数据类型解析。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-05-05
  • 解决Springboot集成Redis集群配置公网IP连接报私网IP连接失败问题

    解决Springboot集成Redis集群配置公网IP连接报私网IP连接失败问题

    在Springboot 集成 Redis集群配置公网IP连接报私网IP连接失败,一直报私有IP连接失败,所以本文小编给大家介绍了如何解决报错问题,如果有遇到相同问题的同学,可以参考阅读本文
    2023-10-10
  • 如何查找YUM安装的JAVA_HOME环境变量详解

    如何查找YUM安装的JAVA_HOME环境变量详解

    这篇文章主要给大家介绍了关于如何查找YUM安装的JAVA_HOME环境变量的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-10-10
  • MyBatis中resultMap和resultType的区别详解

    MyBatis中resultMap和resultType的区别详解

    这篇文章主要介绍了MyBatis中resultMap和resultType的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • 浅谈slf4j中的桥接器是如何运作的

    浅谈slf4j中的桥接器是如何运作的

    这篇文章主要介绍了slf4j中的桥接器是如何运作的,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • MyBatis批量插入数据到Oracle数据库中的两种方式(实例代码)

    MyBatis批量插入数据到Oracle数据库中的两种方式(实例代码)

    本文通过实例代码给大家分享了MyBatis批量插入数据到Oracle数据库中的两种方式,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2017-09-09
  • java中把字符串转成 double的方法

    java中把字符串转成 double的方法

    Java 中可以使用 Double 类中的静态方法 parseDouble() 将一个字符串转换为 double 类型的数值,本文结合实例代码对java字符串转成 double详细讲解,需要的朋友参考下吧
    2023-08-08
  • Java入门基础之常规的命名方法和变量的值及其引用

    Java入门基础之常规的命名方法和变量的值及其引用

    这篇文章主要介绍了Java的命名方法和变量的值及其引用,是Java入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09

最新评论