赞
踩
Java 8 引入了 Stream API,使得处理集合数据变得更加简洁和高效。Stream API 允许开发者以声明式编程风格操作数据集合,而不是使用传统的迭代和条件语句。
Stream 是 Java 8 中的一个新抽象,它允许对集合数据执行各种复杂的操作,例如过滤、映射、规约、收集等。Stream 不存储数据,而是从集合或其他数据源(如数组、I/O channel 等)中获取数据并进行操作。
Stream 的主要特点包括:
Stream 的操作可以分为三类:
一个 Stream 的生命周期可以简单描述为:
Stream 可以通过以下几种方式创建:
- List<String> list = Arrays.asList("a", "b", "c");
- Stream<String> stream = list.stream();
- String[] array = {"a", "b", "c"};
- Stream<String> stream = Arrays.stream(array);
Stream<String> stream = Stream.of("a", "b", "c");
Stream<String> stream = Files.lines(Paths.get("path/to/file.txt"));
中间操作返回一个新的 Stream,它们是延迟执行的,只有在终端操作执行时才会实际进行计算。常用的中间操作包括:
filter
filter
用于对 Stream 中的元素进行过滤,只保留满足条件的元素。
- List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
- Stream<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0);
map
map
用于将 Stream 中的每个元素映射到另一个元素。
- List<String> words = Arrays.asList("Java", "Stream", "API");
- Stream<Integer> wordLengths = words.stream().map(String::length);
flatMap
flatMap
用于将 Stream 中的每个元素映射到一个新的 Stream,并将这些新 Stream 合并成一个 Stream。
- List<List<String>> listOfLists = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("c", "d"));
- Stream<String> flatStream = listOfLists.stream().flatMap(Collection::stream);
distinct
distinct
用于去除 Stream 中的重复元素。
- List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
- Stream<Integer> distinctNumbers = numbers.stream().distinct();
sorted
sorted
用于对 Stream 中的元素进行排序,可以传递一个比较器。
- List<String> words = Arrays.asList("Java", "Stream", "API");
- Stream<String> sortedWords = words.stream().sorted();
终端操作会触发 Stream 的计算,并生成结果或副作用。常用的终端操作包括:
forEach
forEach
用于对 Stream 中的每个元素执行一个动作。
- List<String> words = Arrays.asList("Java", "Stream", "API");
- words.stream().forEach(System.out::println);
toArray
toArray
用于将 Stream 中的元素收集到一个数组中。
- List<String> words = Arrays.asList("Java", "Stream", "API");
- String[] array = words.stream().toArray(String[]::new);
reduce
reduce
用于将 Stream 中的元素通过一个关联函数组合起来,生成一个值。
- List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
- int sum = numbers.stream().reduce(0, Integer::sum);
collect
collect
用于将 Stream 中的元素收集到一个容器中,例如 List、Set 或 Map。
- List<String> words = Arrays.asList("Java", "Stream", "API");
- List<String> upperCaseWords = words.stream().map(String::toUpperCase).collect(Collectors.toList());
count
count
用于返回 Stream 中的元素数量。
- List<String> words = Arrays.asList("Java", "Stream", "API");
- long count = words.stream().count();
findFirst
和 findAny
findFirst
用于返回 Stream 中的第一个元素(如果存在)。
- List<String> words = Arrays.asList("Java", "Stream", "API");
- Optional<String> first = words.stream().findFirst();
findAny
用于返回 Stream 中的任意一个元素(如果存在),常用于并行流。
- List<String> words = Arrays.asList("Java", "Stream", "API");
- Optional<String> any = words.stream().findAny();
anyMatch
、allMatch
和 noneMatch
这三个操作用于检查 Stream 中是否有任意、所有或没有元素满足指定的条件。
- List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
- boolean anyEven = numbers.stream().anyMatch(n -> n % 2 == 0);
- boolean allEven = numbers.stream().allMatch(n -> n % 2 == 0);
- boolean noneNegative = numbers.stream().noneMatch(n -> n < 0);
Java 8 提供了并行流,可以充分利用多核处理器的优势。只需调用 parallelStream
方法即可创建一个并行流。
- List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
- int sum = numbers.parallelStream().reduce(0, Integer::sum);
并行流通过将数据分成多个子流,并在不同的 CPU 核心上并行处理这些子流,然后再合并结果,来提高处理速度。需要注意的是,并行流适合于无状态和无副作用的操作,使用时需小心处理共享变量和同步问题。
Stream API 通常与 lambda 表达式一起使用,使代码更加简洁和易读。例如:
- List<String> words = Arrays.asList("Java", "Stream", "API");
- List<String> upperCaseWords = words.stream().map(word -> word.toUpperCase()).collect(Collectors.toList());
Stream 操作应该是无副作用的,即不应修改外部状态。以下示例展示了一个错误的用法:
- List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
- List<Integer> results = new ArrayList<>();
- numbers.stream().forEach(n -> results.add(n * 2)); // 这样做是错误的
正确的做法是使用终端操作 collect
:
- List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
- List<Integer> results = numbers.stream().map(n -> n * 2).collect(Collectors.toList());
方法引用可以使代码更加简洁。例如,使用方法引用替代 lambda 表达式:
- List<String> words = Arrays.asList("Java", "Stream", "API");
- List<String> upperCaseWords = words.stream().map(String::toUpperCase).collect(Collectors.toList());
并行流在处理大量数据或复杂计算时非常高效,但对于小任务,启动并行计算的开销可能会大于收益。因此,在数据量较小或计算较简单的情况下,优先使用顺序流。
findAny
在终端操作之前调用 findAny
会导致流的中间操作链被截断,进而无法正确执行后续的操作。例如:
- List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
- Optional<Integer> result = numbers.stream().filter(n -> n % 2 == 0).findAny(); // 这样做会中断流
应将 findAny
用作终端操作:
- List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
- Optional<Integer> result = numbers.stream().filter(n -> n % 2 == 0).findAny();
collect
进行结果收集collect
是一个强大的终端操作,可以将流中的元素收集到各种容器中。例如,收集到 List:
- List<String> words = Arrays.asList("Java", "Stream", "API");
- List<String> wordList = words.stream().collect(Collectors.toList());
Collectors
进行复杂收集操作Collectors
提供了多种收集器,可以进行复杂的结果收集。例如,收集到 Map:
- List<String> words = Arrays.asList("Java", "Stream", "API");
- Map<Integer, List<String>> wordLengthMap = words.stream().collect(Collectors.groupingBy(String::length));
Optional
处理可能的空值Stream API 中的某些终端操作会返回 Optional
,例如 findFirst
、findAny
。使用 Optional
可以避免空指针异常:
- List<String> words = Arrays.asList("Java", "Stream", "API");
- Optional<String> firstWord = words.stream().findFirst();
- firstWord.ifPresent(System.out::println);
Java 8 的 Stream API 为集合数据的处理提供了一种高效、简洁的方式。通过理解和掌握 Stream 的基本概念、常用操作以及最佳实践,可以大大提高 Java 开发的生产力和代码质量。
Stream API 不仅支持顺序流,还支持并行流,使得在多核环境下处理大量数据变得更加高效。在实际开发中,合理使用 Stream API 可以显著提升代码的可读性和稳定性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。