为啥 Stream.allMatch() 为空流返回 true?

Posted

技术标签:

【中文标题】为啥 Stream.allMatch() 为空流返回 true?【英文标题】:Why does Stream.allMatch() return true for an empty stream?为什么 Stream.allMatch() 为空流返回 true? 【发布时间】:2015-07-25 05:28:31 【问题描述】:

我和我的同事遇到了一个错误,这是由于我们假设调用allMatch() 的空流将返回false

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

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

【问题讨论】:

这有点奇怪。我们期望如果allMatch 返回true,那么anyMatch 也应该如此。此外,对于空的情况,allMatch(...) == noneMatch(...) 这也很奇怪。 ***说这是惯例:en.wikipedia.org/wiki/Universal_quantification#The_empty_set 关于语法的简短说明:不要将谓词写成i -> i.isValid(),而可以写成Foo::isValid(当然,Foo 是你正在流式传输的任何类) “这个操作以一种离开 monad 的命令方式使用”——我怀疑这会影响任何决定。 【参考方案1】:

这被称为vacuous truth。空集合的所有成员都满足您的条件;毕竟,你能指出一个没有的吗?

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

【讨论】:

天啊,我讨厌布尔逻辑。我想我明白你在说什么。没有负数就是正数,但没有正数不是负数。 @ThomasN。以同样的方式,一个空数字集的乘积是1,而一个空数字集的总和是0。它们是乘法/加法的中性元素。在布尔值的情况下,你有 True and x = xFalse or x = x 因此如果你将 andor 推广到序列(这就是 allany 是)你最终得到 True 和 @987654333 @ 为空的情况,即它们各自的中性元素。 @ThomasN。 anyMatch 测试没有阳性,allMatch 测试没有阴性。 请注意,通过这种方式,您可以做出类似德摩根定律的好事情:stream.allMatch(predicate) 与 !stream.anyMatch(predicate.negate()) 相同。同样,!stream.allMatch(predicate.negate()) 与 stream.anyMatch(predicate) 相同。 @PatrickBard:你可以说“你能指出一个这样做的人吗”,这是完全有效的,但这意味着集合的所有成员都无法满足条件。集合的所有成员都满足条件,集合的所有成员都不满足条件。这些与“集合的所有成员都满足条件是错误的”不同的陈述。就像我说的,这令人困惑。【参考方案2】:

这是另一种思考方式:

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() 则仍需要应用此规则。我们可以使用 && 的“身份元素”来解决这个问题。 true && thing == thing,所以Stream.of().allMatch(it -> it) == true

【讨论】:

【参考方案3】:

当我调用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)【参考方案4】:

看起来它的基础是数学归纳法。对于计算机科学来说,这方面的应用可能是递归算法的基本案例。

如果流为空,则称量化为空满足且始终为真。 Oracle Docs: Stream operations and pipelines

这里的关键是它是“空洞的满足”,从本质上讲,这有点误导。***对此进行了不错的讨论。

在纯数学中,空洞真实的陈述本身通常并不令人感兴趣,但它们经常作为数学归纳证明的基本情况出现。 Wikipedia: Vacuous Truth

【讨论】:

【参考方案5】:

虽然这个问题已经多次正确回答,但我想引入一种更数学的方法。

为此,我想将流视为一个集合(在数学意义上)。那么

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

对应while

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

对应于。

第二部分是假的很明显,因为空集中没有元素。第一个有点棘手。您可以根据定义接受它是正确的,也可以查看其他答案以了解为什么它应该是这样的一些原因。

说明这种差异的一个例子是“所有生活在火星上的人都有 3 条腿”(真)和“有人生活在火星上,有 3 条腿”(假)这样的命题

【讨论】:

【参考方案6】:

在某些情况下解决方法并不美观。

示例:验证是否所有请求都已发送(但某些类型的请求列表可以为空)

public boolean isAllRequestsSent(String type)

        //empty request List
        var count = requestsList.stream().filter(Request::type).count();
        
        if(count <= 0) 
            return false;
        
    
        return requestsList.stream().filter(Request::type).allMatch(RequestData::isSent);
        

【讨论】:

以上是关于为啥 Stream.allMatch() 为空流返回 true?的主要内容,如果未能解决你的问题,请参考以下文章

处理视频标签中的空流

Spring Cloud Turbine 空流

如何在 Java 中创建一个空流?

HttpUrlConnection.getInputStream 在 Android 中返回空流

java连接Sftp服务器后,使用第三方jar com.jcraft.jsch.ChannelSftp;用get()方法获取的InputStream流为空

IO流bufferedReader中 if(br!=null) br.close();啥意思? 不是等于空流结束了才关闭流吗?