你已经了解了如何创建和转换流,接下来将介绍最重要的一点 —— 如何从流数据中找到答案。我们在本节中介绍的方法统称为聚合方法。它们会将流聚合为一个值,以便在程序中使用。聚合方法都是终止操作。当一个流应用了终止操作后,它就不能再应用其他的操作了。
在 Java8 的 Stream API 中,count 方法是一个终端操作(terminal operation),用于计算流中元素的数量。它返回流中元素的个数,这个计数是一个 long 类型的值。
方法定义:
long count()
示例:
统计 list 中元素的个数,如下:
package com.hxstrive.jdk8.stream_api; import java.util.ArrayList; import java.util.List; /** * count 方法 * @author hxstrive.com */ public class StreamCountDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("one"); list.add("two"); list.add("three"); // 跳过流前 2 个元素,并输出这些元素 long count = list.stream().count(); System.out.println("count = " + count); //输出: //count = 3 } }
注意:count 是一个终端操作,这意味着一旦你调用了它,流就会被消费掉,并且你不能再次在这个流上执行其他操作。如果你需要再次使用流中的数据,你需要重新创建流。
Java8 聚合方法是 max 和 min,分别返回流中最大值和最小值。需要注意的一点是 —— 这些方法会返回一个 Optional<T> 值,它可能会封装返回值,也可能表示没有返回 (当流为空时)。在我们之前使用 Java 的时候,这种情况通常会返回 null,但是如果在一个未经完全测试的程序中产生了异常情况,那样会导致抛出空指针异常。在 Java8 中,Optional 类型是一种更好的表示缺少返回值的方式。
方法定义:
// 根据提供的 Comparator返回此流的最大元素 Optional<T> max(Comparator<? super T> comparator) // 根据提供的 Comparator返回此流的最小元素 Optional<T> min(Comparator<? super T> comparator)
示例:
下面示例将获取流中最大值和最小值的值,如下:
package com.hxstrive.jdk8.stream_api; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Optional; /** * max 和 min 方法 * @author hxstrive.com */ public class StreamMaxDemo { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); // 最小值 min Optional<Integer> min = list.stream().min(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); } }); min.ifPresent(System.out::println); // 输出:1 list.stream().min((o1,o2) -> o1.compareTo(o2)).ifPresent(System.out::println); // 输出:1 list.stream().min(Comparator.naturalOrder()).ifPresent(System.out::println); // 输出:1 list.stream().min(Integer::compareTo).ifPresent(System.out::println); // 输出:1 // 最大值 max Optional<Integer> max = list.stream().max(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); } }); max.ifPresent(System.out::println); // 输出:5 list.stream().max((o1,o2) -> o1.compareTo(o2)).ifPresent(System.out::println); // 输出:5 list.stream().max(Comparator.naturalOrder()).ifPresent(System.out::println); // 输出:5 list.stream().max(Integer::compareTo).ifPresent(System.out::println); // 输出:5 } }
findFirst 方法会返回非空集合中的第一个值,它通常与 filter 方法结合起来使用。例如,我们可以找到以字母 t 开头的第一个单词 (如果它存在的话):
package com.hxstrive.jdk8.stream_api; import java.util.Arrays; import java.util.List; import java.util.Optional; /** * findFirst 方法 * @author hxstrive.com */ public class StreamFindFirstDemo { public static void main(String[] args) { List<String> list = Arrays.asList("one", "two","three", "four", "five"); // 获取第一个以 "t" 开头的元素 Optional<String> start = list.stream().filter(e -> { System.out.println("filter -> " + e); return e.startsWith("t"); }).findFirst(); start.ifPresent(System.out::println); //输出: //filter -> one //filter -> two //two } }
从上例输出可知,当找到第一个以“t”开头的元素后,filter 将会停止继续处理后续元素,即 filter 是可以中断的。
如果你想找到所有匹配的元素 (而不只是第一个),那么可以使用 findAny 方法。这个方法在对流进行并行执行时十分有效,因为只要在任何片段中发现了第一个匹配元素,都会结束整个计算。例如:
package com.hxstrive.jdk8.stream_api; import java.util.Arrays; import java.util.List; import java.util.Optional; /** * findAny 方法 * @author hxstrive.com */ public class StreamFindAnyDemo { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David"); // 串行流示例 Optional<String> anyNameSerial = names.stream().findAny(); anyNameSerial.ifPresent(System.out::println); // 可能输出 Alice、Bob、Charlie 或 David 中的任意一个 // 并行流示例 Optional<String> anyNameParallel = names.parallelStream().findAny(); anyNameParallel.ifPresent(System.out::println); // 在并行流中,输出可能是随机的 } }
如果你只希望知道流中是否含有匹配元素,可以使用 anyMatch 方法。该方法可以接受一个 Predicate 参数,因此你不需要使用 filter 方法。例如:
package com.hxstrive.jdk8.stream_api; import java.util.Arrays; import java.util.List; /** * anyMatch 方法 * @author hxstrive.com */ public class StreamAnyMatchDemo { public static void main(String[] args) { List<String> list = Arrays.asList("one", "two","three", "four", "five"); // 同步执行 // 判断流中是否存在 “two” 字符串,true 表示存在,false 表示不存在 boolean flag = list.stream().anyMatch(s -> "two".endsWith(s)); System.out.println(flag); // true // 并行执行 boolean flag2 = list.parallelStream().anyMatch(s -> "two".equals(s)); System.out.println(flag2); // true } }
Java8 中还提供了两个方法 allMatch 和 noneMatch,它们分别在所有元素均匹配和没有元素匹配 Predicate 时返回 true。虽然这些方法总是会检查整个流,但是仍可以通过并行执行来提高速度。