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 API 允许你以函数式风格对集合进行过滤和映射。其核心思想是将数据源(如 List、Set 等)转换为 Stream,然后应用一系列中间操作(如 filter、map)来转换数据,最后通过终端操作(如 collect、forEach)来产生结果。
立即学习“Java免费学习笔记(深入)”;
如何使用 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 还有很多其他常用的终端操作,例如:
这些终端操作可以满足各种不同的需求,你可以根据实际情况选择合适的终端操作。
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中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号