Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to match stream elements but return false if non exists?

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.

How can I do this?

Example code:

public boolean validate(Stream<Whatever> stream) {
  // Problem: returns **true** if stream empty.
  // How can **false** be returned if stream is empty?
  return stream.allMatch(Whatever::someCheck);
}
like image 436
AlikElzin-kilaka Avatar asked Sep 06 '17 12:09

AlikElzin-kilaka


People also ask

How do you check if a stream is empty or not?

You have to consume the stream to find out if it's empty. That's the point of Stream's semantics (laziness). To check that the stream is not empty you have to attempt to consume at least one element. At that point the stream has lost its "virginity" and cannot be consumed again from the start.

How do I return a Boolean in Java 8?

The boolean returned represents the value true if the string argument is not null and is equal, ignoring case, to the string "true" . Example: Boolean. parseBoolean("True") returns true . Example: Boolean.

What does stream () return?

Generating Streams With Java 8, Collection interface has two methods to generate a Stream. stream() − Returns a sequential stream considering collection as its source. parallelStream() − Returns a parallel Stream considering collection as its source.

Is streams better than for loop?

Remember that loops use an imperative style and Streams a declarative style, so Streams are likely to be much easier to maintain. If you have a small list, loops perform better. If you have a huge list, a parallel stream will perform better.


2 Answers

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.

like image 139
Holger Avatar answered Oct 05 '22 23:10

Holger


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)).

like image 32
Jonathan Avatar answered Oct 06 '22 01:10

Jonathan