3.10 使用anyMatchallMatchnoneMatch方法

问题

用户希望确定流中是否有元素匹配 Predicate,或全部元素匹配 Predicate,或没有元素匹配 Predicate

方案

使用 java.util.stream.Stream 接口定义的 anyMatchallMatchnoneMatch 方法,每种方法返回一个布尔值。

讨论

anyMatchallMatchnoneMatch 方法的签名如下:

  1. boolean anyMatch(Predicate<? super T> predicate)
  2. boolean allMatch(Predicate<? super T> predicate)
  3. boolean noneMatch(Predicate<? super T> predicate)

每种方法的用途不言自明。我们以质数计算器为例进行说明。在大于或等于 2 的自然

数中,如果一个数无法被除 1 和该数自身之外的其他数整除,则这个数为质数(prime number),否则为合数(composite number)。

如例 3-51 所示,为验证某个数是否为质数,一种简单的做法是对从 2 开始到该数平方根的所有数做取模运算,然后取整。

例 3-51 质数校验

  1. public boolean isPrime(int num) {
  2. int limit = (int) (Math.sqrt(num) + 1);
  3. return num == 2 || num > 1 && IntStream.range(2, limit)
  4. .noneMatch(divisor -> num % divisor == 0);
  5. }

❶ 校验上限

❷ 使用 noneMatch 方法

借由 noneMatch 方法,质数校验易如反掌。

BigInteger 类与质数

java.math.BigInteger 类定义的 isProbablePrime 方法很有意思,其签名为:

  1. boolean isProbablePrime(int certainty)

如果 isProbablePrime 方法返回 false,则值显然为合数;如果返回 truecertainty 参数就派上用场了。

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 针对质数计算的测试

  1. private Primes calculator = new Primes();
  2.  
  3. @Test
  4. public void testIsPrimeUsingAllMatch() throws Exception {
  5. assertTrue(IntStream.of(2, 3, 5, 7, 11, 13, 17, 19)
  6. .allMatch(calculator::isPrime));
  7. }
  8.  
  9. @Test
  10. public void testIsPrimeWithComposites() throws Exception {
  11. assertFalse(Stream.of(4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20)
  12. .anyMatch(calculator::isPrime));
  13. }

❶ 为简单起见,使用 allMatch 方法

❷ 使用合数进行测试

第一个测试调用已知质数流中的 allMatch 方法(参数为 Predicate),仅当所有值均为质数时返回 true

第二个测试对一个合数集合使用 anyMatch 方法,并认定集合中的数字均不满足谓词。

anyMatchallMatchnoneMatch 方法能方便地对特定条件下的值流进行校验。

不过,有一个可能引起问题的边缘条件应予注意。如例 3-53 所示,三种方法在作用于空流(empty stream)时的行为不那么直观。

例 3-53 针对空流的测试

  1. @Test
  2. public void emptyStreamsDanger() throws Exception {
  3. assertTrue(Stream.empty().allMatch(e -> false));
  4. assertTrue(Stream.empty().noneMatch(e -> true));
  5. assertFalse(Stream.empty().anyMatch(e -> true));
  6. }

根据 Javadoc 的描述,对于 allMatchnoneMatch 方法,“流为空将返回 true 且不再评估谓词”,因此两种方法中的谓词可以是任何值。而对于 anyMatch 方法,流为空将返回 false,这可能导致错误诊断异常困难,所以应用时务须谨慎。

3.10 使用anyMatch、allMatch与noneMatch方法 - 图1 如果流为空,无论提供的谓词是什么,allMatchnoneMatch 方法将返回 true,而 anyMatch 方法将返回 false。三种方法在流为空时都不会评估任何提供的谓词。

另见

有关 Predicate 接口的讨论请参见范例 2.3。