你已经了解了如何创建和转换流,接下来将介绍最重要的一点 —— 如何从流数据中找到答案。我们在本节中介绍的方法统称为聚合方法。它们会将流聚合为一个值,以便在程序中使用。聚合方法都是终止操作。当一个流应用了终止操作后,它就不能再应用其他的操作了。
在 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。虽然这些方法总是会检查整个流,但是仍可以通过并行执行来提高速度。