为什么Stream.allMatch()对于空流返回true?


84

我和我的同事有一个错误,这是由于我们假设allMatch()会返回空流调用而导致的false

if (myItems.allMatch(i -> i.isValid()) { 
    //do something
}

当然,假设和不阅读文档是我们的错。但是我不明白的是为什么allMatch()空流的默认行为会返回true。这是什么原因呢?像anyMatch()(相反地返回false)一样,此操作以命令式使用,它离开了monad,并且可能在if语句中使用了。考虑到这些事实,是否有任何理由使大多数用途都需要allMatch()默认为true空流?


4
有点奇怪。我们希望,如果allMatch返回true,那么也应该如此anyMatch。另外,对于空的情况,allMatch(...) == noneMatch(...)这也很奇怪。
Radiodef


语法方面i -> i.isValid(),请Foo::isValidFoo
稍等片刻

2
“此操作以必不可少的方式使用,它离开了monad”-我怀疑此因素是否会影响任何决策。
user253751 2015年

Answers:


115

这就是所谓的虚无事实。空集合的所有成员都满足您的条件;毕竟,您能指出一个没有的吗?

同样,anyMatch返回false,因为找不到匹配条件的集合中的元素。这使很多人感到困惑,但事实证明,这是为空集定义“任何”和“全部”的最有用和一致的方法。


3
哎呀,我讨厌布尔逻辑。我想我明白你在说什么。缺少否定是积极的,但是没有积极性不是消极的。
tmn

13
@ThomasN。以相同的方式,一组空数字的乘积的值为,而一组空数字1的总和为0。它们是乘法/加法的中性元素。在布尔值的情况下,你有True and x = xFalse or x = x,因此,如果你推广and,并or于序列(这是allany是)你结束了TrueFalse为空的情况下,即它们各自的中性元素。
2015年

9
@ThomasN。anyMatch测试是否存在阳性,allMatch测试是否存在阴性。
user253751 2015年

3
请注意,通过这种方式,您可以完成De Morgan定律之类的出色工作:stream.allMatch(predicate)与!stream.anyMatch(predicate.negate())相同。同样,!stream.allMatch(predicate.negate())与stream.anyMatch(predicate)相同。
汉斯(Hans)

1
@PatrickBard:您可以说“您能指出一个能做到的事吗”,这是完全有效的,但是这意味着该集合的所有成员都不能满足该条件。集合的所有成员均满足条件,并且集合的所有成员均不满足条件。这些声明与“集合的所有成员都满足条件是错误的”不同。就像我说的那样,这令人困惑。
user2357112支持Monica18年

10

这是考虑此问题的另一种方法:

allMatch()&&什么sum()+

考虑以下逻辑语句:

IntStream.of(1, 2).sum() + 3 == IntStream.of(1, 2, 3).sum()
IntStream.of(1).sum() + 2 == IntStream.of(1, 2).sum()

这是有道理的,因为这sum()只是的概括+。但是,当您再删除一个元素时会发生什么?

IntStream.of().sum() + 1 == IntStream.of(1).sum()

我们可以看到,IntStream.of().sum()以特定的方式定义或空序列的总和是有意义的。这为我们提供了求和的“身份元素”,或者当它添加到某物时没有作用的值(0)。

我们可以将相同的逻辑应用于Boolean代数。

Stream.of(true, true).allMatch(it -> it) == Stream.of(true).allMatch(it -> it) && true

更笼统地说:

stream.concat(Stream.of(thing)).allMatch(it -> it) == stream.allMatch(it -> it) && thing

如果stream = Stream.of()这样,该规则仍然需要适用。我们可以使用&&的“ identity element”来解决这个问题。true && thing == thing所以Stream.of().allMatch(it -> it) == true


6

当我调用list.allMatch(或其他语言的类似物)时,我想检测是否有任何项list与谓词不匹配。如果没有任何项目,则可能没有任何匹配项。我的以下逻辑会选择项目,并期望它们与谓词匹配。对于空列表,我将不选择任何项目,逻辑仍然是正确的。

如果allMatch返回false空列表怎么办?

我直接的逻辑会失败:

 if (!myList.allMatch(predicate)) {
   throw new InvalidDataException("Some of the items failed to match!");
 }
 for (Item item : myList) { ... }

我需要记住用代替支票!myList.empty() && !myList.allMatch()

简而言之,allMatch返回true空列表不仅在逻辑上是合理的,而且还位于执行的快乐路径上,需要较少的检查。


您可能的意思是if (!allMatch)
亚述2015年

3

看起来它的基础是数学归纳法。对于计算机科学,此方法的应用可能是递归算法的基本情况。

如果流为空,则称其为空空的定量,并且始终为真。Oracle Docs:流操作和管道

这里的关键是它“天生地满足”,从本质上说,这是令人误解的。维基百科对此进行了不错的讨论。

在纯数学中,虚无的真实陈述本身通常并不令人感兴趣,但是它们经常作为数学归纳法证明的基础。维基百科:空洞的真相


1

尽管这个问题已经被正确地乘以倍数了,但是我想引入一种更数学的方法。

为此,我想将流视为一个Set(在数学意义上)。然后

emptyStream.allMatch(x-> p(x))

对应于在此处输入图片说明while

emtpyStream.anyMatch(x -> p(x))

对应于在此处输入图片说明

第二部分为假是很明显的,因为空集中没有元素。第一个比较棘手。根据定义,您可以接受它为真,也可以出于某些原因考虑其他答案。

一个示例来说明这种差异,例如“所有人类都住在火星上有3条腿”(true)和“有人类住在火星上有3条腿”(false)这样的命题

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.