3.10 使用anyMatch、allMatch与noneMatch方法
问题
用户希望确定流中是否有元素匹配 Predicate,或全部元素匹配 Predicate,或没有元素匹配 Predicate。
方案
使用 java.util.stream.Stream 接口定义的 anyMatch、allMatch 与 noneMatch 方法,每种方法返回一个布尔值。
讨论
anyMatch、allMatch 与 noneMatch 方法的签名如下:
- boolean anyMatch(Predicate<? super T> predicate)
- boolean allMatch(Predicate<? super T> predicate)
- boolean noneMatch(Predicate<? super T> predicate)
每种方法的用途不言自明。我们以质数计算器为例进行说明。在大于或等于 2 的自然
数中,如果一个数无法被除 1 和该数自身之外的其他数整除,则这个数为质数(prime number),否则为合数(composite number)。
如例 3-51 所示,为验证某个数是否为质数,一种简单的做法是对从 2 开始到该数平方根的所有数做取模运算,然后取整。
例 3-51 质数校验
- public boolean isPrime(int num) {
- int limit = (int) (Math.sqrt(num) + 1); ➊
- return num == 2 || num > 1 && IntStream.range(2, limit)
- .noneMatch(divisor -> num % divisor == 0); ➋
- }
❶ 校验上限
❷ 使用 noneMatch 方法
借由 noneMatch 方法,质数校验易如反掌。
BigInteger类与质数
java.math.BigInteger类定义的isProbablePrime方法很有意思,其签名为:
- boolean isProbablePrime(int certainty)
如果
isProbablePrime方法返回false,则值显然为合数;如果返回true,certainty参数就派上用场了。
certainty的值表示调用程序可以容忍的不确定性。如果isProbablePrime方法返回true,则一个数实际为质数的概率将超过1 - 1/2^{certainty}。因此,certainty等于 2 意味着概率为 0.75,certainty等于 3 意味着概率为 0.875,certainty等于 4 意味着概率为 0.9375,等于 5 意味着概率为 0.96875,以此类推。
certainty参数的值越大,isProbablePrime方法的执行时间越长。
例 3-52 显示了质数校验的两种方案。
例 3-52 针对质数计算的测试
- private Primes calculator = new Primes();
- @Test ➊
- public void testIsPrimeUsingAllMatch() throws Exception {
- assertTrue(IntStream.of(2, 3, 5, 7, 11, 13, 17, 19)
- .allMatch(calculator::isPrime));
- }
- @Test ➋
- public void testIsPrimeWithComposites() throws Exception {
- assertFalse(Stream.of(4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20)
- .anyMatch(calculator::isPrime));
- }
❶ 为简单起见,使用 allMatch 方法
❷ 使用合数进行测试
第一个测试调用已知质数流中的 allMatch 方法(参数为 Predicate),仅当所有值均为质数时返回 true。
第二个测试对一个合数集合使用 anyMatch 方法,并认定集合中的数字均不满足谓词。
anyMatch、allMatch 与 noneMatch 方法能方便地对特定条件下的值流进行校验。
不过,有一个可能引起问题的边缘条件应予注意。如例 3-53 所示,三种方法在作用于空流(empty stream)时的行为不那么直观。
例 3-53 针对空流的测试
- @Test
- public void emptyStreamsDanger() throws Exception {
- assertTrue(Stream.empty().allMatch(e -> false));
- assertTrue(Stream.empty().noneMatch(e -> true));
- assertFalse(Stream.empty().anyMatch(e -> true));
- }
根据 Javadoc 的描述,对于 allMatch 和 noneMatch 方法,“流为空将返回 true 且不再评估谓词”,因此两种方法中的谓词可以是任何值。而对于 anyMatch 方法,流为空将返回 false,这可能导致错误诊断异常困难,所以应用时务须谨慎。
如果流为空,无论提供的谓词是什么,
allMatch和noneMatch方法将返回true,而anyMatch方法将返回false。三种方法在流为空时都不会评估任何提供的谓词。
另见
有关 Predicate 接口的讨论请参见范例 2.3。
如果流为空,无论提供的谓词是什么,