Java中对stream的操作
在Java中,Stream API 提供了一种高效处理数据的方式。Stream操作可以分为中间操作(Intermediate Operations)和终端操作(Terminal Operations)。中间操作可以进一步分为无状态(Stateless)和有状态(Stateful)操作,而终端操作可以分为短路(Short-circuiting)和非短路(Non-short-circuiting)操作。
1. 中间操作(Intermediate Operations)
-
无状态(Stateless)中间操作:
-
这些操作是不保留状态的。它们不需要关于其他元素的信息就可以处理每个传入的元素。例如,
map和filter就是无状态操作。每个元素的处理只依赖于该元素本身,而与流中其他元素无关。 -
示例:
map:对流中的每个元素应用一个函数。filter:根据给定的谓词过滤流中的元素。
-
-
有状态(Stateful)中间操作:
-
这些操作需要关于之前处理过的元素的信息。例如,排序操作需要知道之前的元素来确定当前元素的正确位置。
-
示例:
sorted:对流中的元素进行排序。distinct:返回一个只包含不同元素的流。
-
2. 终端操作(Terminal Operations)
-
短路(Short-circuiting)终端操作:
-
这些操作不一定需要处理整个流就可以得到结果。它们可以在达到某种条件时“短路”并提前结束处理流程。
-
示例:
findFirst:返回流中的第一个元素。anyMatch:检查流中的元素是否有任何一个符合给定的谓词。
-
-
非短路(Non-short-circuiting)终端操作:
-
这些操作需要处理流中的所有元素才能得到结果。
-
示例:
forEach:对流中的每个元素执行一个操作。collect:将流转换为其他形式,例如集合。
-
中间操作是惰性的,只有在执行终端操作时,所有中间操作才会被执行。这种设计使得操作可以优化执行,例如通过合并或消除不必要的处理步骤。
无状态(Stateless)中间操作举例
1. filter
filter操作使用一个谓词来测试每个元素,仅保留满足条件的元素。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");
List<String> filteredNames = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
// 结果: ["Alice"]
2. map
map操作对流中的每个元素应用一个函数,并将其转换成另一种形式。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");
List<Integer> nameLengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
// 结果: [5, 3, 7, 5]
3. flatMap
flatMap操作将流中的每个元素转换成另一个流,然后将这些流连接起来成为一个流。
List<List<String>> listOfLists = Arrays.asList(
Arrays.asList("Alice", "Bob"),
Arrays.asList("Charlie", "Diana")
);
List<String> flatList = listOfLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// 结果: ["Alice", "Bob", "Charlie", "Diana"]
4. peek
peek操作会对每个元素执行操作,主要用于调试。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<Integer> processedNumbers = numbers.stream()
.peek(number -> System.out.println("Before map: " + number))
.map(number -> number * 2)
.peek(number -> System.out.println("After map: " + number))
.collect(Collectors.toList());
// 控制台输出元素处理前后的状态
5. distinct
distinct操作返回一个流,其中包含了原始流中不同的元素。
List<Integer> numbersWithDuplicates = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
List<Integer> uniqueNumbers = numbersWithDuplicates.stream()
.distinct()
.collect(Collectors.toList());
// 结果: [1, 2, 3, 4, 5]
6. sorted
sorted操作返回一个流,其中的元素按自然顺序排序,也可以提供一个自定义的比较器。
List<String> names = Arrays.asList("Charlie", "Diana", "Alice", "Bob");
List<String> sortedNames = names.stream()
.sorted()
.collect(Collectors.toList());
// 结果: ["Alice", "Bob", "Charlie", "Diana"]
7. limit
limit操作截取流,使其最大长度不超过给定数量。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> limitedNumbers = numbers.stream()
.limit(3)
.collect(Collectors.toList());
// 结果: [1, 2, 3]
8. skip
skip操作返回一个丢弃了原始流的前N个元素的流。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> skippedNumbers = numbers.stream()
.skip(2)
.collect(Collectors.toList());
// 结果: [3, 4, 5, 6]
有状态(Stateful)中间操作举例
1. distinct
distinct操作返回一个流,其中包含了原始流中不同的元素。这需要维护一个状态来记录已经出现过的元素。
List<Integer> numbersWithDuplicates = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
List<Integer> uniqueNumbers = numbersWithDuplicates.stream()
.distinct()
.collect(Collectors.toList());
// 结果: [1, 2, 3, 4, 5]
2. sorted
sorted操作返回一个流,其中的元素按自然顺序排序,也可以提供一个自定义的比较器。排序操作需要知道之前的元素来确定当前元素的正确位置。
List<String> names = Arrays.asList("Charlie", "Diana", "Alice", "Bob");
List<String> sortedNames = names.stream()
.sorted()
.collect(Collectors.toList());
// 结果: ["Alice", "Bob", "Charlie", "Diana"]
3. limit
虽然limit在某些情况下可以被认为是无状态的,但在涉及无序流时(如并行流),它可能需要维护状态以确保限制操作的正确性。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> limitedNumbers = numbers.stream()
.limit(5)
.collect(Collectors.toList());
// 结果: [1, 2, 3, 4, 5]
4. skip
和limit类似,skip在处理无序流时也可能需要维护状态。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> skippedNumbers = numbers.stream()
.skip(5)
.collect(Collectors.toList());
// 结果: [6, 7, 8, 9, 10]
短路(Short-circuiting)终端操作举例
1. anyMatch
anyMatch操作检查流中的元素是否有任何一个符合给定的谓词。如果找到符合条件的元素,它会立即返回true并结束处理。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");
boolean hasNameStartingWithA = names.stream()
.anyMatch(name -> name.startsWith("A"));
// 结果: true
2. allMatch
allMatch操作检查流中的所有元素是否都符合给定的谓词。如果所有元素都符合条件,或者流是空的,它返回true。
List<Integer> numbers = Arrays.asList(2, 4, 6, 8, 10);
boolean allEven = numbers.stream()
.allMatch(n -> n % 2 == 0);
// 结果: true
3. noneMatch
noneMatch操作检查流中的元素是否都不符合给定的谓词。如果没有任何元素符合条件,它返回true。
List<Integer> numbers = Arrays.asList(2, 4, 6, 8, 10);
boolean noneOdd = numbers.stream()
.noneMatch(n -> n % 2 != 0);
// 结果: true
4. findFirst
findFirst操作返回流中的第一个元素,如果流为空,则返回一个空的Optional。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");
Optional<String> first = names.stream()
.findFirst();
// 结果: Optional[Alice]
5. findAny
findAny操作返回流中的任意一个元素。在并行流中使用时,这通常会比findFirst更高效。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");
Optional<String> any = names.stream()
.findAny();
// 结果: Optional[某个元素,比如Alice]
非短路(Non-short-circuiting)终端操作举例
1. forEach
forEach操作对流中的每个元素执行给定的操作。它通常用于产生副作用(如打印)。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");
names.stream()
.forEach(System.out::println);
// 输出每个名字
2. collect
collect操作是一个非常强大的终端操作,它可以将流转换成其他形式,如集合。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");
List<String> list = names.stream()
.collect(Collectors.toList());
// 将流转换为列表
3. count
count操作返回流中的元素个数。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");
long count = names.stream()
.count();
// 结果: 4
4. reduce
reduce操作将流中的元素组合起来,使用一个累积函数,并返回一个可选的累积结果。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
// 结果: 15
5. max 和 min
这些操作分别返回流中的最大值和最小值,根据提供的比较器。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
OptionalInt max = numbers.stream()
.mapToInt(Integer::intValue)
.max();
// 结果: OptionalInt[5]
OptionalInt min = numbers.stream()
.mapToInt(Integer::intValue)
.min();
// 结果: OptionalInt[1]
6. summaryStatistics
对于原始类型流(如IntStream、LongStream和DoubleStream),summaryStatistics会收集统计信息,如总数、总和、最小值、最大值和平均值。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
IntSummaryStatistics stats = numbers.stream()
.mapToInt(Integer::intValue)
.summaryStatistics();
// 获取统计信息
7. forEachOrdered
forEachOrdered保留了流的遇到顺序,即使在并行流中也是如此。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");
names.parallelStream()
.forEachOrdered(System.out::println);
// 按照原始列表的顺序输出每个名字