当前位置: 首页 > java >正文

Java中的流详解

下面文章详细介绍了 Java 8 中引入的 Stream API 的概念、核心特性、常用操作、并行流以及最佳实践,供你参考。


Java中的流详解

Java 8 带来了一个非常重要的新特性——Stream API。通过流,Java 程序员可以以声明式方式对集合和数组等数据源进行操作,极大地简化数据处理代码,同时提高代码的可读性和表达能力。本文将详细解析 Java 中的 Stream 流,从基本概念、操作种类、流的创建和执行机制,到并行流的使用和一些最佳实践。


1. 流的基本概念

什么是流?
在 Java 中,流(Stream) 是一组来自数据源的元素集合。与传统集合不同,流不会自己存储数据;它只是在对数据源(例如集合、数组、I/O、生成器等)进行计算时,提供一种高效、声明式的聚合操作方式。

流的特点:

  • 声明式编程风格
    通过一系列中间操作和终端操作对数据进行转换,而不用关注底层的迭代逻辑。

  • 中间操作和终端操作

    • 中间操作(Intermediate Operations):例如 filter()map()sorted() 等,这些操作返回一个新的流,并具有惰性求值的特性。
    • 终端操作(Terminal Operations):例如 collect()forEach()reduce()count() 等,一旦执行,整个流的计算链条才会被触发,并返回一个非流的结果。
  • 不可重复消费
    流一旦执行了终端操作后就“关闭”了,如果需要再次处理,需要重新创建流。


2. 流的创建

流的创建通常有几种常见方式:

2.1 从集合或数组生成流

  • 集合产生流
    通过 Collection 接口中的 stream() 方法生成一个串行流,或 parallelStream() 生成一个并行流。

    List<String> list = Arrays.asList("张三", "李四", "王五");
    Stream<String> stream = list.stream();
    
  • 数组产生流
    使用 Arrays.stream(T[] array)Stream.of() 方法生成流。

    String[] arr = {"Java", "Python", "C++"};
    Stream<String> stream = Arrays.stream(arr);
    // 或者
    Stream<String> stream2 = Stream.of(arr);
    

2.2 从生成器生成流

  • 无限流
    使用 Stream.iterate()Stream.generate() 可以生成无限流,通常需要借助 limit() 限制元素数。

    // 生成从0开始的偶数序列,取前10个
    Stream<Integer> evenNumbers = Stream.iterate(0, n -> n + 2).limit(10);
    evenNumbers.forEach(System.out::println);// 使用 generate() 生成随机数
    Stream<Double> randomNumbers = Stream.generate(Math::random).limit(5);
    randomNumbers.forEach(System.out::println);
    

3. 流的常见操作

流操作分为中间操作与终端操作。下面分别介绍几种常用操作。

3.1 中间操作

  • filter(Predicate predicate)
    筛选符合条件的数据。

    List<String> list = Arrays.asList("张三", "李四", "王五");
    list.stream().filter(name -> name.startsWith("张")).forEach(System.out::println);
    
  • map(Function<T, R> mapper)
    将每个元素转换成另一种形式,例如获取字符串长度。

    List<String> words = Arrays.asList("Java", "Stream", "API");
    words.stream().map(String::length).forEach(System.out::println);
    
  • flatMap(Function<T, Stream> mapper)
    将多个流合并为一个流,常用于处理嵌套集合。

  • sorted()
    自然排序或基于 Comparator 进行排序。

    List<Integer> numbers = Arrays.asList(5, 3, 8, 1, 9);
    numbers.stream().sorted().forEach(System.out::println);
    
  • distinct()
    去重操作,返回一个包含不重复元素的新流。

  • limit(long maxSize) 与 skip(long n)
    分别用于截取前 n 个数据和跳过前 n 个元素。

    Stream<Integer> stream = Stream.iterate(1, n -> n + 1);
    stream.skip(5).limit(10).forEach(System.out::println);
    

3.2 终端操作

  • forEach(Consumer action)
    遍历流中每个元素,通常用于打印或产生副作用。

  • collect(Collector<T, A, R> collector)
    将流中的数据收集到列表、集合或其他数据结构中。常用的 Collectors 工具类提供了很多预定义的收集器。

    List<String> nonEmpty = Arrays.asList("Java", "", "Python", "C++").stream().filter(s -> !s.isEmpty()).collect(Collectors.toList());
    System.out.println(nonEmpty);
    
  • reduce(BinaryOperator accumulator)
    将流中的元素逐步归约为一个值,例如求和或求最大值。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    int sum = numbers.stream().reduce(0, Integer::sum);
    System.out.println(sum);
    
  • count()、min()、max()
    返回流中元素的计数、最小值或最大值。


4. 并行流(Parallel Stream)

Java 8 的流除了支持顺序处理,还支持利用多核处理器的并行计算。通过调用 parallelStream() 或在现有流上调用 parallel() 方法,即可让流的处理以并行方式执行。

List<String> list = Arrays.asList("Java", "Python", "C++", "JavaScript", "Ruby");
long count = list.parallelStream().filter(s -> s.contains("a")).count();
System.out.println("包含字母 'a' 的数量:" + count);

注意事项:

  • 并行流的开销适用于数据量较大且算法拆分均衡的场景;若数据量较小或操作较简单,使用并行流有时反而会降低效率。
  • 对共享资源的访问需要格外注意线程安全问题。

5. Stream API 的优势与注意事项

优势

  • 声明式编程风格
    代码更加简洁清晰,适合聚合操作链式表达,使程序看起来更像 SQL 查询。

  • 高效聚合计算
    Stream API 内部采用了延迟求值和短路运算的机制(例如 findFirst()anyMatch() 等终端操作),能在满足条件时立即停止后续操作,从而节省计算资源。

  • 支持并行处理
    利用并行流可以简化多线程编程,提高大规模数据处理的性能。

注意事项

  • 流操作不可重复使用
    流一旦执行了终端操作就关闭了,若需要多次处理必须重新生成流。

  • 调试相对困难
    由于流操作中的中间操作多采用 lambda 表达式,使得代码调试和阅读有时不如传统迭代方式直观。

  • 并行流的额外开销
    在某些场景下,数据分割、线程协作需要额外开销,不一定总能提高性能。因此,适用场景需要根据实际数据量、操作复杂度等综合考虑。


6. 总结

Java 8 的 Stream API 为集合数据处理提供了全新的思路,让代码更具声明式风格和函数式编程的优雅。通过中间操作和终端操作的组合,开发者可以轻松实现数据过滤、映射、排序、聚合等常见操作,同时借助并行流充分利用多核处理器。
但在享受其便利性的同时,也要注意流的不可重复使用、调试跟踪以及并行操作可能带来的额外开销。

希望这篇文章能帮助你深入理解 Java 中的流及其各种操作,在日常开发中更高效地处理数据。如果你对某个操作细节或应用场景有更多疑问,欢迎在评论中讨论交流!


http://www.xdnf.cn/news/7287.html

相关文章:

  • vue3 vite 路由
  • 容器化-K8s-镜像仓库使用和应用
  • Ubuntu Desktop QEMU/KVM中使用Ubuntu Server 22.04配置k8s集群
  • k8s1.27集群部署mysql8.0双主双从
  • psotgresql18 源码编译安装
  • AutoMouser - 单次AI调用铸就高效自动化脚本
  • mariadb 升级 (通过yum)
  • 排序复习/下(C语言版)
  • vuex的基本使用
  • 零基础设计模式——设计模式入门
  • 下载和导出文件名称乱码问题
  • SVG 与 Canvas 技术调研对比
  • BeanFactory和FactoryBean的区别
  • 【Vue篇】潮汐中的生命周期观测站​
  • 10.14 Function Calling与Tool Calling终极解析:大模型工具调用的5大核心差异与实战优化
  • 基于python版本secsgem源码开发gem,应该如何设置devicetype、connectmode
  • Python训练营打卡 Day30
  • Docker安装MySQL数据库-单机版
  • 不锈钢保温容器行业2025数据分析报告
  • Hi3516CV610车牌识别算法源码之——实时从sensor采集视频 识别车牌
  • 使用PHP对接东南亚、日本、印度和印度尼西亚股票数据源
  • 医疗行业手持终端:推动医疗智能化的关键力量
  • 网络Tips20-007
  • lammps后处理:堆垛层错和孪晶的数量统计
  • 养生指南:解锁健康生活新方式
  • 阿里云web端直播(前端部分)
  • kotlin flow的两种SharingStarted策略的区别
  • 远程医疗结合贴肤芯片技术对体育院校学生提升运动表现的路径分析
  • 五分钟完成PolarDB替换postgresql
  • codeup添加流水线docker自动化部署