首页 > Java > 正文

Java中如何用Stream对集合进行过滤和映射

冰火之心
发布: 2025-06-12 16:42:01
原创
369人浏览过

java stream 是一种声明式处理集合的方式,通过操作链实现数据过滤、转换等。其核心思想是将数据源如 list、set 转换为 stream,接着使用 filter() 方法接收 predicate 接口以判断元素是否保留来过滤数据,如用 n -> n > 10 过滤出大于 10 的数字;随后可调用 map() 方法接收 function 接口以转换元素,如 string::touppercase 将字符串转为大写;这些中间操作具备延迟执行特性,只有终端操作如 collect()、foreach()、sum() 等被调用时才执行,且可并行处理,通过 parallelstream() 分解任务提升性能,但需评估线程开销;此外,stream 只能使用一次,消耗后需重新创建;异常处理可通过 try-catch 捕获或 optional 类型安全处理 null 值,确保流操作的健壮性。

Java中如何用Stream对集合进行过滤和映射

Java Stream提供了一种声明式的方式来处理集合数据,核心在于通过一系列操作链,实现数据的过滤、转换等。简单来说,就是把集合变成一个“流”,然后像流水线一样加工它。

Java中如何用Stream对集合进行过滤和映射

解决方案

Java中如何用Stream对集合进行过滤和映射

Java Stream API 允许你以函数式风格对集合进行过滤和映射。其核心思想是将数据源(如 List、Set 等)转换为 Stream,然后应用一系列中间操作(如 filter、map)来转换数据,最后通过终端操作(如 collect、forEach)来产生结果。

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

Java中如何用Stream对集合进行过滤和映射

如何使用 Stream 进行过滤?

过滤操作 filter() 接收一个 Predicate 函数式接口,该接口定义了一个 test() 方法,用于判断元素是否应该被包含在结果流中。

例如,假设你有一个整数列表,想要过滤出所有大于 10 的数字:

List<Integer> numbers = Arrays.asList(1, 5, 12, 8, 15, 3);

List<Integer> filteredNumbers = numbers.stream()
                                        .filter(n -> n > 10)
                                        .collect(Collectors.toList());

System.out.println(filteredNumbers); // 输出: [12, 15]
登录后复制

这里,n -> n > 10 就是一个 Predicate 表达式,它判断数字 n 是否大于 10。collect(Collectors.toList()) 则是将过滤后的 Stream 转换回 List。

如何使用 Stream 进行映射?

映射操作 map() 接收一个 Function 函数式接口,该接口定义了一个 apply() 方法,用于将一个元素转换为另一个元素。

例如,假设你有一个字符串列表,想要将每个字符串转换为大写:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

List<String> upperCaseNames = names.stream()
                                   .map(String::toUpperCase)
                                   .collect(Collectors.toList());

System.out.println(upperCaseNames); // 输出: [ALICE, BOB, CHARLIE]
登录后复制

String::toUpperCase 是一个方法引用,它等价于 s -> s.toUpperCase(),将字符串 s 转换为大写。

如何同时进行过滤和映射?

你可以将 filter() 和 map() 操作链式调用,先过滤再映射,或者先映射再过滤,取决于你的需求。

例如,假设你有一个 Person 对象列表,想要获取所有年龄大于 20 岁的人的名字,并将名字转换为大写:

class Person {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

List<Person> people = Arrays.asList(
    new Person("Alice", 25),
    new Person("Bob", 18),
    new Person("Charlie", 30)
);

List<String> upperCaseNames = people.stream()
                                     .filter(p -> p.getAge() > 20)
                                     .map(Person::getName)
                                     .map(String::toUpperCase)
                                     .collect(Collectors.toList());

System.out.println(upperCaseNames); // 输出: [ALICE, CHARLIE]
登录后复制

这里,我们先使用 filter() 过滤出年龄大于 20 岁的人,然后使用 map() 获取他们的名字,再使用 map() 将名字转换为大写。

Stream 的延迟执行特性是什么?

Stream 的一个重要特性是延迟执行(Lazy Evaluation)。中间操作(如 filter() 和 map())不会立即执行,而是会等到终端操作(如 collect())被调用时才一起执行。

这种延迟执行的特性可以提高性能,因为 Stream 可以避免不必要的计算。例如,如果你的 Stream 只需要处理前几个元素,那么 Stream 可以只计算前几个元素,而不需要计算整个集合。

Stream 可以并行处理吗?

Stream 可以通过调用 parallelStream() 方法来并行处理。并行处理可以将任务分解成多个子任务,并在多个线程上同时执行,从而提高处理速度。

例如,假设你想要计算一个大型数字列表中所有大于 10 的数字的总和:

List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
    numbers.add(i);
}

int sum = numbers.parallelStream()
                 .filter(n -> n > 10)
                 .mapToInt(Integer::intValue)
                 .sum();

System.out.println(sum);
登录后复制

这里,我们使用 parallelStream() 创建一个并行 Stream,然后使用 filter() 过滤出大于 10 的数字,再使用 mapToInt() 将 Integer 对象转换为 int 类型,最后使用 sum() 计算总和。

需要注意的是,并行处理并非总是能提高性能。如果任务过于简单,或者数据量太小,并行处理可能会因为线程切换的开销而降低性能。因此,在使用并行 Stream 之前,需要仔细评估其性能影响。

Stream 只能使用一次吗?

是的,Stream 只能使用一次。一旦你对 Stream 执行了终端操作,Stream 就被“消耗”掉了,不能再次使用。如果你需要再次使用相同的数据,你需要重新创建一个新的 Stream。这其实也符合“流”的概念,用完就没了。

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

除了 collect()、forEach() 和 sum() 之外,Stream 还有很多其他常用的终端操作,例如:

  • count(): 统计 Stream 中元素的数量。
  • min(): 找出 Stream 中最小的元素。
  • max(): 找出 Stream 中最大的元素。
  • findFirst(): 找出 Stream 中第一个元素。
  • findAny(): 找出 Stream 中任意一个元素。
  • anyMatch(): 判断 Stream 中是否存在至少一个元素满足指定条件。
  • allMatch(): 判断 Stream 中是否所有元素都满足指定条件。
  • noneMatch(): 判断 Stream 中是否没有任何元素满足指定条件。
  • reduce(): 将 Stream 中的元素聚合为一个结果。

这些终端操作可以满足各种不同的需求,你可以根据实际情况选择合适的终端操作。

Stream 的异常处理应该如何进行?

在 Stream 操作中,如果某个操作抛出异常,整个 Stream 流程将会中断。为了避免这种情况,你需要对 Stream 操作进行异常处理。

一种常见的做法是使用 try-catch 块来捕获异常,并在 catch 块中进行处理。例如:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", null);

names.stream()
     .map(name -> {
         try {
             return name.toUpperCase();
         } catch (NullPointerException e) {
             System.err.println("遇到空指针异常: " + e.getMessage());
             return null; // 或者返回一个默认值
         }
     })
     .filter(Objects::nonNull) // 过滤掉 null 值
     .forEach(System.out::println);
登录后复制

在这个例子中,我们使用 try-catch 块来捕获 NullPointerException 异常,并在 catch 块中打印错误信息,然后返回 null 值。最后,我们使用 filter(Objects::nonNull) 来过滤掉 null 值。

另一种做法是使用 Optional 来处理可能为空的值。例如:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", null);

names.stream()
     .map(name -> Optional.ofNullable(name)
                          .map(String::toUpperCase)
                          .orElse(null))
     .filter(Objects::nonNull)
     .forEach(System.out::println);
登录后复制

在这个例子中,我们使用 Optional.ofNullable(name) 将 name 转换为 Optional 对象。如果 name 为 null,则 Optional 对象为空。然后,我们使用 map(String::toUpperCase) 将 Optional 对象中的字符串转换为大写。如果 Optional 对象为空,则 map() 方法不会执行。最后,我们使用 orElse(null) 来返回 Optional 对象中的值,如果 Optional 对象为空,则返回 null。

总而言之,Java Stream 是一种强大的工具,可以让你以简洁、高效的方式处理集合数据。掌握 Stream API 的使用方法,可以大大提高你的编程效率。

以上就是Java中如何用Stream对集合进行过滤和映射的详细内容,更多请关注php中文网其它相关文章!

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

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

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

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