I have a stream and would like to check if all match a filter. If all match, return true
.
But, if the stream is empty, I\'d like to return false
return !stream.filter(Whatever::someCheck).collect(Collectors.toList()).isEmpty()
I already added two answers I now removed but the problem kept nagging on me so I gave it a third try and this time it works including keeping allMatch
. Here is the code for method validate
.
public static boolean validate(Stream<Whatever> stream) {
final boolean[] streamNotEmpty = new boolean[1];
return stream.filter(elem -> {
streamNotEmpty[0] = true;
return true;
}).allMatch(Whatever::someCheck) && streamNotEmpty[0];
}
A shorter version would be
public static boolean validate(Stream<Whatever> stream) {
final boolean[] streamNotEmpty = new boolean[1];
return stream.allMatch(elem -> {
streamNotEmpty[0] = true;
return elem.someCheck();
}) && streamNotEmpty[0];
}
The idea behind this is that we want to know if there was at least one element in the stream, so I created a final boolean[]
where the value is changed within the filter-call. So at the end of the stream's processing we can check the stream for being empty or not and act accordingly when we return the result.
Here is a complete class including testcases to prove that - this time finally - I provided a valid solution.
import java.util.ArrayList;
import java.util.stream.Stream;
public class StreamTest {
public static boolean validate(Stream<Whatever> stream) {
final boolean[] streamNotEmpty = new boolean[1];
return stream.allMatch(elem -> {
streamNotEmpty[0] = true;
return elem.someCheck();
}) && streamNotEmpty[0];
}
static class Whatever {
private boolean checkResult;
public Whatever() {
this(false);
}
public Whatever(boolean b) {
this.checkResult = b;
}
public boolean someCheck() {
return checkResult;
}
}
public final static void main(String[] args) {
ArrayList<Whatever> list = new ArrayList<>();
System.out.println("empty list: " + validate(list.stream()));
System.out.println("empty list parallel: " + validate(list.parallelStream()));
for (int i = 0; i < 10000; i++) {
list.add(new Whatever(true));
}
System.out.println("non empty true list: " + validate(list.stream()));
System.out.println("non empty true list parallel: " + validate(list.parallelStream()));
for (int i = 0; i < 10000; i += 1000) {
list.add(new Whatever(false));
}
System.out.println("non empty false list: " + validate(list.stream()));
System.out.println("non empty false list parallel: " + validate(list.parallelStream()));
}
}
The output when executing it is:
empty list: false
empty list parallel: false
non empty true list: true
non empty true list parallel: true
non empty false list: false
non empty false list parallel: false
You could use
public boolean validate(Stream<Whatever> stream) {
return stream.map(Whatever::someCheck).reduce(Boolean::logicalAnd).orElse(false);
}
which expresses the intent. We map each element to a boolean
value expressing whether it matches and reducing all of them with a logical and operation which will yield true
iff all of them were true
. reduce
will return an empty Optional
if there were no elements, which we map to false
using orElse(false)
, as intended.
The only disadvantage is that this is non short-circuiting, i.e. does not stop immediately at the first non-matching element.
A solution still supporting short-circuiting might be a bit more evolved:
public boolean validate(Stream<Whatever> stream) {
boolean parallel = stream.isParallel();
Spliterator<Whatever> sp = stream.spliterator();
if(sp.getExactSizeIfKnown() == 0) return false;
Stream.Builder<Whatever> b = Stream.builder();
if(!sp.tryAdvance(b)) return false;
return Stream.concat(b.build(), StreamSupport.stream(sp, parallel))
.allMatch(Whatever::someCheck);
}
This is a variant of Eugene’s answer, but it doesn’t loose characteristics or parallelism and might be a bit more efficient in general.
If you are OK with losing your characteristics and parallelism, this for example:
public static boolean validate(Stream<String> stream) {
Iterator<String> it = stream.iterator();
if (!it.hasNext()) {
return false;
}
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, 0), false)
.peek(System.out::println)
.allMatch(x -> x.contains("a"));
}
The following code will work (I tested it).
public static boolean validate(Stream<Whatever> stream) {
return stream.reduce((whatever1, whatever2) -> Whatever.someCheck(whatever1) ? whatever2 : whatever1)
.map(Whatever::someCheck).orElse(false);
}
How it works? We use reduce
operation to check that every element matches the predicate, if it fails, we keep returning the failing one (in ternary operation). At the end, we map the reduced Whatever
object to boolean, and if it's true: then all matched and this is not empty (orElse(false)
).
Try peek
boolean[] flag = new boolean[1];
return stream.peek(t -> flag[0] = true).allMatch(Whatever::someCheck) && flag[0]