为什么 Stream.allMatch() return 对于空流为真?
Why does Stream.allMatch() return true for an empty stream?
我和我的同事有一个错误,这是由于我们假设调用 allMatch()
的空流会 return false
。
if (myItems.allMatch(i -> i.isValid()) {
//do something
}
当然,假设而不阅读文档是我们的错。但我不明白的是为什么空流的默认 allMatch()
行为 returns true
。这样做的原因是什么?与 anyMatch()
(相反,return 为 false)一样,此操作以脱离 monad 的命令式方式使用,并可能在 if
语句中使用。考虑到这些事实,有什么理由可以让 allMatch()
在空流上默认为 true
对于大多数用途来说是可取的?
这被称为vacuous truth。空集合的所有成员都满足您的条件;毕竟,你能指出一个没有的吗?
同样,anyMatch
returns false
,因为您无法在集合中找到符合条件的元素。这让很多人感到困惑,但事实证明这是为空集定义 "any" 和 "all" 的最有用和最一致的方法。
当我调用 list.allMatch
(或其在其他语言中的类似物)时,我想检测 list
中的任何项目是否与谓词不匹配。如果没有项目,none 可能无法匹配。我的以下逻辑将选择项目并期望它们与谓词匹配。对于空列表,我不会选择任何项目,逻辑仍然合理。
如果 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 the stream is empty, the quantification is said to be vacuously satisfied and is always true. Oracle Docs: Stream operations and pipelines
这里的关键是 "vacuously satisfied" 本质上有点误导。维基百科对此有很好的讨论。
In pure mathematics, vacuously true statements are not generally of interest by themselves, but they frequently arise as the base case of proofs by mathematical induction. Wikipedia: Vacuous Truth
这是另一种思考方式:
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()
或空数字序列的总和是有意义的。这给了我们求和的 "identity element",或者当添加到某物时没有效果的值 (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
.
虽然这个问题已经正确回答了很多次,但我想引入一种更数学化的方法。
为此,我想将流视为一个集合(在数学意义上)。然后
emptyStream.allMatch(x-> p(x))
对应于而
emtpyStream.anyMatch(x -> p(x))
对应.
第二部分是假的很明显,因为空集中没有元素。第一个有点棘手。您可以根据定义接受它是真实的,也可以出于某些原因查看其他答案。
说明这种差异的一个例子是诸如“所有生活在火星上的人都有 3 条腿”(正确)和“有一个人生活在火星上有 3 条腿”(错误)这样的命题
在某些情况下,解决方法并不完美。
示例:验证是否所有请求都已发送(但某些类型的请求列表可以为空)
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);
}
我和我的同事有一个错误,这是由于我们假设调用 allMatch()
的空流会 return false
。
if (myItems.allMatch(i -> i.isValid()) {
//do something
}
当然,假设而不阅读文档是我们的错。但我不明白的是为什么空流的默认 allMatch()
行为 returns true
。这样做的原因是什么?与 anyMatch()
(相反,return 为 false)一样,此操作以脱离 monad 的命令式方式使用,并可能在 if
语句中使用。考虑到这些事实,有什么理由可以让 allMatch()
在空流上默认为 true
对于大多数用途来说是可取的?
这被称为vacuous truth。空集合的所有成员都满足您的条件;毕竟,你能指出一个没有的吗?
同样,anyMatch
returns false
,因为您无法在集合中找到符合条件的元素。这让很多人感到困惑,但事实证明这是为空集定义 "any" 和 "all" 的最有用和最一致的方法。
当我调用 list.allMatch
(或其在其他语言中的类似物)时,我想检测 list
中的任何项目是否与谓词不匹配。如果没有项目,none 可能无法匹配。我的以下逻辑将选择项目并期望它们与谓词匹配。对于空列表,我不会选择任何项目,逻辑仍然合理。
如果 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 the stream is empty, the quantification is said to be vacuously satisfied and is always true. Oracle Docs: Stream operations and pipelines
这里的关键是 "vacuously satisfied" 本质上有点误导。维基百科对此有很好的讨论。
In pure mathematics, vacuously true statements are not generally of interest by themselves, but they frequently arise as the base case of proofs by mathematical induction. Wikipedia: Vacuous Truth
这是另一种思考方式:
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()
或空数字序列的总和是有意义的。这给了我们求和的 "identity element",或者当添加到某物时没有效果的值 (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
.
虽然这个问题已经正确回答了很多次,但我想引入一种更数学化的方法。
为此,我想将流视为一个集合(在数学意义上)。然后
emptyStream.allMatch(x-> p(x))
对应于
emtpyStream.anyMatch(x -> p(x))
对应
第二部分是假的很明显,因为空集中没有元素。第一个有点棘手。您可以根据定义接受它是真实的,也可以出于某些原因查看其他答案。
说明这种差异的一个例子是诸如“所有生活在火星上的人都有 3 条腿”(正确)和“有一个人生活在火星上有 3 条腿”(错误)这样的命题
在某些情况下,解决方法并不完美。
示例:验证是否所有请求都已发送(但某些类型的请求列表可以为空)
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);
}