首页 > Java > 正文

Java中Stream的用法_Java中Stream的操作指南

冰火之心
发布: 2025-06-29 21:15:02
原创
238人浏览过

stream是java 8引入的声明式数据处理特性,通过中间操作和终端操作简化集合处理。1. 创建方式包括:从集合调用stream()或parallelstream();2. 使用arrays.stream()转换数组;3. stream.of()直接传元素;4. generate()生成无限流需配合limit();5. iterate()创建有序无限流也需limit()。常用中间操作有filter过滤、map映射、flatmap展平嵌套结构、distinct去重、sorted排序、peek调试、limit截断、skip跳过。终端操作如foreach遍历、toarray转数组、reduce归约、collect收集结果、min/max找极值、count计数、anymatch/allmatch/nonematch判断匹配、findfirst/findany获取元素。适合使用并行流的场景包括大数据量、复杂计算、无依赖数据、cpu密集型任务且需线程安全。与传统循环相比,stream为声明式、内部迭代更简洁并支持并行处理。

Java中Stream的用法_Java中Stream的操作指南

Stream是Java 8引入的一个强大的特性,它允许你以声明式的方式处理集合数据,简化代码并提高效率。可以把它看作是数据流,你可以在上面执行各种操作,例如过滤、映射、排序等,最终得到想要的结果。

Java中Stream的用法_Java中Stream的操作指南

Stream的操作可以分为两种:中间操作和终端操作。中间操作返回一个新的Stream,允许你链式调用多个操作。终端操作则会消费Stream,产生一个结果或副作用。

Java中Stream的用法_Java中Stream的操作指南

如何创建Stream?

创建Stream的方法有很多,最常见的几种方式包括:

立即学习Java免费学习笔记(深入)”;

Java中Stream的用法_Java中Stream的操作指南
  1. 从集合创建: 这是最常用的方式,直接调用Collection接口的stream()或parallelStream()方法即可。

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    Stream<String> stream = names.stream(); // 创建顺序流
    Stream<String> parallelStream = names.parallelStream(); // 创建并行流
    登录后复制
  2. 从数组创建: 使用Arrays.stream()方法可以将数组转换为Stream。

    int[] numbers = {1, 2, 3, 4, 5};
    IntStream stream = Arrays.stream(numbers);
    登录后复制
  3. 使用Stream.of()方法: 可以直接将多个元素作为参数传递给Stream.of()方法来创建Stream。

    Stream<String> stream = Stream.of("Apple", "Banana", "Orange");
    登录后复制
  4. 使用Stream.generate()方法: 可以使用Stream.generate()方法生成一个无限流,需要提供一个Supplier函数来生成元素。注意: 无限流需要配合limit()方法来限制流的大小,否则会无限循环。

    Stream<Double> randomStream = Stream.generate(Math::random).limit(10); // 生成10个随机数的流
    登录后复制
  5. 使用Stream.iterate()方法: 可以使用Stream.iterate()方法生成一个有序的无限流,需要提供一个初始值和一个UnaryOperator函数来生成后续元素。同样,需要配合limit()方法来限制流的大小。

    Stream<Integer> evenNumbers = Stream.iterate(0, n -> n + 2).limit(10); // 生成10个偶数的流
    登录后复制

Stream的常用中间操作有哪些?

中间操作会返回一个新的Stream,允许你链式调用多个操作,对数据进行转换和过滤。常见的中间操作包括:

  1. filter(Predicate predicate): 过滤Stream中的元素,只保留满足predicate条件的元素。

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Anna");
    Stream<String> filteredStream = names.stream().filter(name -> name.startsWith("A")); // 过滤出以"A"开头的名字
    登录后复制
  2. map(Function mapper): 将Stream中的每个元素映射为另一个元素,可以使用mapper函数进行转换。

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    Stream<Integer> nameLengths = names.stream().map(String::length); // 将名字映射为名字的长度
    登录后复制
  3. flatMap(Function> mapper): 将Stream中的每个元素映射为一个Stream,然后将所有Stream连接成一个Stream。这个操作通常用于处理嵌套的集合。想象一下,你有一个List>,你想把它变成一个List,flatMap就派上用场了。

    List<List<String>> nestedList = Arrays.asList(
            Arrays.asList("a", "b"),
            Arrays.asList("c", "d", "e")
    );
    Stream<String> flattenedStream = nestedList.stream().flatMap(List::stream); // 将嵌套的List展平
    登录后复制
  4. distinct(): 去除Stream中重复的元素。

    List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3);
    Stream<Integer> distinctStream = numbers.stream().distinct(); // 去除重复的数字
    登录后复制
  5. sorted(): 对Stream中的元素进行排序。可以传入Comparator来自定义排序规则。

    List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
    Stream<String> sortedStream = names.stream().sorted(); // 默认按字母顺序排序
    Stream<String> sortedStreamByLength = names.stream().sorted(Comparator.comparingInt(String::length)); // 按名字长度排序
    登录后复制
  6. peek(Consumer action): 对Stream中的每个元素执行action操作,但不会改变Stream的内容。这个操作通常用于调试,可以在Stream处理过程中查看元素的值。

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    Stream<String> peekedStream = names.stream().peek(System.out::println); // 打印每个名字
    登录后复制
  7. limit(long maxSize): 截断Stream,只保留前maxSize个元素。

    Stream<Integer> numbers = Stream.iterate(1, n -> n + 1);
    Stream<Integer> limitedStream = numbers.limit(10); // 只保留前10个数字
    登录后复制
  8. skip(long n): 跳过Stream中的前n个元素。

    Stream<Integer> numbers = Stream.iterate(1, n -> n + 1);
    Stream<Integer> skippedStream = numbers.skip(5); // 跳过前5个数字
    登录后复制

Stream的常用终端操作有哪些?

终端操作会消费Stream,产生一个结果或副作用。常见的终端操作包括:

  1. forEach(Consumer action): 对Stream中的每个元素执行action操作。

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    names.stream().forEach(System.out::println); // 打印每个名字
    登录后复制
  2. toArray(): 将Stream中的元素转换为数组。

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    String[] nameArray = names.stream().toArray(String[]::new); // 转换为String数组
    登录后复制
  3. reduce(BinaryOperator accumulator): 将Stream中的元素归约为一个值。可以提供一个初始值,也可以不提供。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    int sum = numbers.stream().reduce(0, Integer::sum); // 计算所有数字的和,初始值为0
    Optional<Integer> product = numbers.stream().reduce(Integer::sum); // 计算所有数字的积,没有初始值,返回Optional
    登录后复制
  4. collect(Collector collector): 将Stream中的元素收集到集合中。可以使用Collectors类提供的各种收集器,例如toList()、toSet()、toMap()等。

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    List<String> nameList = names.stream().collect(Collectors.toList()); // 收集到List
    Set<String> nameSet = names.stream().collect(Collectors.toSet()); // 收集到Set
    Map<String, Integer> nameMap = names.stream().collect(Collectors.toMap(name -> name, String::length)); // 收集到Map,key为名字,value为名字的长度
    登录后复制
  5. min(Comparator comparator): 返回Stream中的最小值。需要提供一个Comparator来比较元素。

    List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9);
    Optional<Integer> min = numbers.stream().min(Integer::compare); // 找到最小的数字
    登录后复制
  6. max(Comparator comparator): 返回Stream中的最大值。需要提供一个Comparator来比较元素。

    List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9);
    Optional<Integer> max = numbers.stream().max(Integer::compare); // 找到最大的数字
    登录后复制
  7. count(): 返回Stream中元素的个数。

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    long count = names.stream().count(); // 计算名字的个数
    登录后复制
  8. anyMatch(Predicate predicate): 判断Stream中是否存在任意一个元素满足predicate条件。

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    boolean anyMatch = names.stream().anyMatch(name -> name.startsWith("A")); // 是否存在以"A"开头的名字
    登录后复制
  9. allMatch(Predicate predicate): 判断Stream中是否所有元素都满足predicate条件。

    List<String> names = Arrays.asList("Alice", "Anna", "Amy");
    boolean allMatch = names.stream().allMatch(name -> name.startsWith("A")); // 是否所有名字都以"A"开头
    登录后复制
  10. noneMatch(Predicate predicate): 判断Stream中是否没有元素满足predicate条件。

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    boolean noneMatch = names.stream().noneMatch(name -> name.startsWith("Z")); // 是否没有名字以"Z"开头
    登录后复制
  11. findFirst(): 返回Stream中的第一个元素。

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    Optional<String> first = names.stream().findFirst(); // 找到第一个名字
    登录后复制
  12. findAny(): 返回Stream中的任意一个元素。在并行流中,findAny()的效率通常比findFirst()更高。

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    Optional<String> any = names.parallelStream().findAny(); // 找到任意一个名字
    登录后复制

什么时候应该使用并行流?

并行流可以利用多核CPU的优势,提高处理数据的效率。但是,并行流并非总是最佳选择。以下是一些需要考虑的因素:

  • 数据量: 当数据量足够大时,并行流才能体现出优势。对于小数据量,并行流的开销可能超过其带来的性能提升。
  • 操作的复杂性: 对于简单的操作,并行流的优势可能不明显。对于复杂的操作,并行流可以显著提高效率。
  • 数据之间的依赖性: 如果数据之间存在依赖性,并行流可能会导致错误的结果。
  • CPU密集型操作: 并行流更适合CPU密集型操作,例如计算、排序等。对于IO密集型操作,并行流的优势可能不明显。
  • 线程安全: 使用并行流时,需要确保操作是线程安全的。

一般来说,当数据量较大,操作比较复杂,且数据之间没有依赖性时,可以考虑使用并行流。

Stream和传统循环有什么区别

Stream和传统循环的主要区别在于:

  • 声明式 vs 命令式: Stream采用声明式编程,你只需要描述你想做什么,而不需要关心如何实现。传统循环采用命令式编程,你需要明确地指定每一步的操作。
  • 内部迭代 vs 外部迭代: Stream采用内部迭代,由Stream API负责迭代数据。传统循环采用外部迭代,你需要自己控制迭代过程。
  • 代码简洁性: Stream可以简化代码,使代码更易读易懂。
  • 并行处理: Stream可以方便地进行并行处理,提高效率。

总的来说,Stream提供了一种更简洁、更高效的数据处理方式。在合适的场景下,使用Stream可以显著提高代码的可读性和性能。

以上就是Java中Stream的用法_Java中Stream的操作指南的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号