另一个常见的数据处理套路是看看数据集中的某些元素是否匹配一个给定的属性。Stream API通过allMatch、anyMatch、noneMatch、findFirst和findAny方法提供了这样的工具。
版本1
allMatch 匹配所有
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().allMatch(i -> i > 3)) {
System.out.println("值都大于3");
}
anyMatch 匹配其中一个
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().anyMatch(i -> i > 3)) {
System.out.println("存在大于3的值");
}
等同于
for (Integer i : integerList) {
if (i > 3) {
System.out.println("存在大于3的值");
break;
}
}
noneMatch 全部不匹配
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().noneMatch(i -> i > 3)) {
System.out.println("值都小于3");
}
查找
提供了两种查找方式
1、findFirst 查找第一个
//查找到第一个大于 3 的元素并打印
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();
2、findAny 随机查找一个
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findAny();
通过 findAny 方法查找到其中一个大于三的元素并打印,因为内部进行优化的原因,当找到第一个满足大于三的元素时就结束,该方法结果和 findFirst 方法结果一样。提供 findAny 方法是为了更好的利用并行流,findFirst 方法在并行上限制更多
另外一个版本
检查谓词是否至少匹配一个元素
anyMatch方法可以回答“流中是否有一个元素能匹配给定的谓词”。比如,你可以用它来看看菜单里面是否有素食可选择:
if(menu.stream().anyMatch(Dish::isVegetarian)){
System.out.println(“The menu is (somewhat) vegetarian friendly!!”);
}
anyMatch方法返回一个boolean,因此是一个终端操作。
检查谓词是否匹配所有元素
allMatch方法的工作原理和anyMatch类似,但它会看看流中的元素是否都能匹配给定的谓词。比如,你可以用它来看看菜品是否有利健康(即所有菜的热量都低于1000卡路里):
boolean isHealthy = menu.stream()
.allMatch(d -> d.getCalories() < 1000);
noneMatch
和allMatch相对的是noneMatch。它可以确保流中没有任何元素与给定的谓词匹配。比如,你可以用noneMatch重写前面的例子:
boolean isHealthy = menu.stream()
.noneMatch(d -> d.getCalories() >= 1000);
anyMatch、allMatch和noneMatch这三个操作都用到了我们所谓的短路,这就是大家熟悉的Java中&&和||运算符短路在流中的版本。
短路求值
有些操作不需要处理整个流就能得到结果。例如,假设你需要对一个用and连起来的大布尔表达式求值。不管表达式有多长,你只需找到一个表达式为false,就可以推断整个表达式将返回false,所以用不着计算整个表达式。这就是短路。
对于流而言,某些操作(例如allMatch、anyMatch、noneMatch、findFirst和findAny)不用处理整个流就能得到结果。只要找到一个元素,就可以有结果了。同样,limit也是一个短路操作:它只需要创建一个给定大小的流,而用不着处理流中所有的元素。在碰到无限大小的流的时候,这种操作就有用了:它们可以把无限流变成有限流。我们会在5.7节中介绍无限流的例子。
查找元素
findAny方法将返回当前流中的任意元素。它可以与其他流操作结合使用。比如,你可能想找到一道素食菜肴。你可以结合使用filter和findAny方法来实现这个查询:
Optional
menu.stream()
.filter(Dish::isVegetarian)
.findAny();
流水线将在后台进行优化使其只需走一遍,并在利用短路找到结果时立即结束。不过慢着,代码里面的Optional是个什么玩意儿?
Optional简介
Optional
isPresent()将在Optional包含值的时候返回true, 否则返回false。
ifPresent(Consumer
T get()会在值存在时返回值,否则抛出一个NoSuchElement异常。
T orElse(T other)会在值存在时返回值,否则返回一个默认值。
例如,在前面的代码中你需要显式地检查Optional对象中是否存在一道菜可以访问其名称:
menu.stream()
.filter(Dish::isVegetarian)
.findAny() ←─返回一个Optional
.ifPresent(d -> System.out.println(d.getName()); ←─如果包含一个值就打印它,否则什么都不做
查找第一个元素
有些流有一个出现顺序(encounter order)来指定流中项目出现的逻辑顺序(比如由List或排序好的数据列生成的流)。对于这种流,你可能想要找到第一个元素。为此有一个findFirst方法,它的工作方式类似于findany。例如,给定一个数字列表,下面的代码能找出第一个平方能被3整除的数:
List
Optional
someNumbers.stream()
.map(x -> x * x)
.filter(x -> x % 3 == 0)
.findFirst(); // 9
何时使用findFirst和findAny
你可能会想,为什么会同时有findFirst和findAny呢?答案是并行。找到第一个元素在并行上限制更多。如果你不关心返回的元素是哪个,请使用findAny,因为它在使用并行流时限制较少。