Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Stream.allMatch() return true for an empty stream?

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?

like image 855
tmn Avatar asked May 13 '15 18:05

tmn


People also ask

Does stream work on empty list?

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.

What does anyMatch return?

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.

What does anyMatch do?

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.

What is the difference between the anyMatch () and findAny () stream methods?

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.


2 Answers

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.

like image 150
user2357112 supports Monica Avatar answered Sep 23 '22 22:09

user2357112 supports Monica


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.

like image 32
Hans Avatar answered Sep 23 '22 22:09

Hans