My colleague and I had a bug that was due to our assumption that an empty stream calling allMatch()
would return false
.
if (myItems.allMatch(i -> i.isValid()) { //do something }
Of course, it is kind of our fault for assuming and not reading documentation. But what I don't understand is why the default allMatch()
behavior for an empty stream returns true
. What was the reasoning for this? Like the anyMatch()
(which contrarily returns false), this operation is used in an imperative way that departs the monad and probably used in an if
statement. Considering those facts, is there any reason why having allMatch()
default to true
on an empty stream be desirable for majority of uses?
toList()) , you'll always get an output List (you'll never get null ). If the Stream is empty (and it doesn't matter if it's empty due to the source of the stream being empty, or due to all the elements of the stream being filtered out prior to the terminal operation), the output List will be empty too.
anyMatch() returns true if any of the elements in a stream matches the given predicate. If the stream is empty or if there's no matching element, it returns false . allMatch() returns true only if ALL elements in the stream match the given predicate.
anyMatch() It returns whether any elements of this stream match the provided predicate. It may not evaluate the predicate on all elements if not necessary for determining the result.
Stream#anyMatch() returns a boolean while Stream#findAny() returns an object which matches the predicate. They almost do the same work. anyMatch is a short-circuit operation, but filter will always process the whole stream.
This is known as vacuous truth. All members of an empty collection satisfy your condition; after all, can you point to one that doesn't?
Similarly, anyMatch
returns false
, because you can't find an element of your collection that does match the condition. This is confusing to a lot of people, but it turns out to be the most useful and consistent way to define "any" and "all" for empty sets.
Here's another way to think about this:
allMatch()
is to &&
what sum()
is to +
Consider the following logical statements:
IntStream.of(1, 2).sum() + 3 == IntStream.of(1, 2, 3).sum() IntStream.of(1).sum() + 2 == IntStream.of(1, 2).sum()
This makes sense because sum()
is just a generalization of +
. However, what happens when you remove one more element?
IntStream.of().sum() + 1 == IntStream.of(1).sum()
We can see that it makes sense to define IntStream.of().sum()
, or the sum of an empty sequence of numbers, in a particular way. This gives us the "identity element" of summation, or the value that, when added to something, has no effect (0
).
We can apply the same logic to Boolean
algebra.
Stream.of(true, true).allMatch(it -> it) == Stream.of(true).allMatch(it -> it) && true
More generically:
stream.concat(Stream.of(thing)).allMatch(it -> it) == stream.allMatch(it -> it) && thing
If stream = Stream.of()
then this rule still needs to apply. We can use the "identity element" of && to solve this. true && thing == thing
, so Stream.of().allMatch(it -> it) == true
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With