另一个常见的数据处理套路是看看数据集中的某些元素是否匹配一个给定的属性。StreamAPI
通过allMatch
、anyMatch
、noneMatch
、findFirst
和findAny
方法提供了这样的工具。
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);
和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也是一个短路操作:它只需要创建一个给定大小的流,而用不着处理流中所有的元素。在碰到无限大小的流的时候,这种操作就有用了:它们可以把无限流变成有限流。
findAny
方法将返回当前流中的任意元素。它可以与其他流操作结合使用。比如,你可能想找到一道素食菜肴。你可以结合使用filter
和findAny
方法来实现这个查询:
Optional<Dish> dish =
menu.stream()
.filter(Dish::isVegetarian)
.findAny();
流水线将在后台进行优化使其只需走一遍,并在利用短路找到结果时立即结束。
Optional
类(java.util.optional
)是一个容器类,代表一个值存在或不存在。在上面的代码中,findAny
可能什么元素都没找到。Java 8
的库设计人员引入了optional
,这样就不用返回众所周知容易出问题的null
了。先了解一下optional
里面几种可以迫使你显式地检查值是否存在或处理值不存在的情形的方法。
isPresent()
将在optional
包含值的时候返回true
,否则返回false
。ifPresent(Consumer block)
会在值存在的时候执行给定的代码块。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<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByThree = someNumbers.stream()
.map(x -> x*x)
.filter(x -> x 3 == 0)
.findFirst();// 9
何时使用findFirst和findAny
你可能会想,为什么会同时有findFirst和findAny呢?答案是并行。找到第一个元素在并行上限制更多。如果你不关心返回的元素是哪个,请使用findAny,因为它在使用并行流时限制较少。