13

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);
}
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
AlikElzin-kilaka
  • 34,335
  • 35
  • 194
  • 277

6 Answers6

18

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.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 3
    You can replace `Whatever::someCheck` with whatever compound validation. Chaining multiple `filter` calls is not different to combining the conditions via `&&`, e.g. in a lambda expression. And other solutions than creating a compound predicate are not possible with your initial approach either. After you’ve called `allMatch`, the stream has been consumed and no other validations are possible. – Holger Sep 06 '17 at 15:29
  • Is there a way to keep the `allMatch()`? I think it's more descriptive than `map().reduce(Boolean::logicalAnd)`. – AlikElzin-kilaka Sep 06 '17 at 16:22
  • Well, the second variant has the `allMatch` invocation. There is no other clean way, as `allMatch` simply is an opaque terminal operation that doesn’t support to get combined with another evaluation. – Holger Sep 06 '17 at 16:56
  • @Holger oh wow! that second approach is well so *you*. very nice! I had no idea a `Builder` is a `Consumer` and this makes implementation such a breeze here – Eugene Sep 06 '17 at 20:19
  • [`spliterator`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/BaseStream.html#spliterator--) is officially a terminal operation though. I guess that means the implementation is not required to preserve lazyness. – Jorn Vernee Sep 06 '17 at 21:10
  • @Jorn Vernee: the `validate` method is a terminal operation as a whole; when the method retruns, the stream can’t be used anymore. I’m not sure which implication to the laziness you have in mind. – Holger Sep 07 '17 at 08:22
3

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

Jonathan
  • 772
  • 4
  • 11
  • …which is not different to my `.map(Whatever::someCheck).reduce(Boolean::logicalAnd).orElse(false)`, except that you might call `someCheck` multiple times on the same object, however, the total number of `someCheck` calls will be the same. – Holger Sep 06 '17 at 15:40
  • @Holger I've posted my answer few seconds before yours. But you're right, that's why I already upvoted your answer too. – Jonathan Sep 06 '17 at 15:44
  • 4
    Your variant is an interesting pointer to the possibility to query the first failed element too. – Holger Sep 06 '17 at 16:00
2

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"));

}
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • 2
    [`iterator`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/BaseStream.html#iterator--) is officially a terminal operation though, although it might not be in practice. – Jorn Vernee Sep 06 '17 at 13:19
  • @JornVernee yes, I was thinking in the direction of how many elements you want to examine from that Stream – Eugene Sep 06 '17 at 13:24
  • 2
    You can solve the deficiencies of your solution by using a `Spliterator` instead of an `Iterator`. [My answer](https://stackoverflow.com/a/46077388/2711488) contains such a solution. The remaining question is whether short-circuiting is worth loosing the clarity of the non-short-circuiting solution… – Holger Sep 06 '17 at 14:09
0

Try peek

boolean[] flag = new boolean[1];
return stream.peek(t -> flag[0] = true).allMatch(Whatever::someCheck) && flag[0]
123-xyz
  • 619
  • 4
  • 5
  • thanks, update to new `boolean[1]`. can you give a sample which scenario it might NOT work for sequential stream? and when/why are not guaranteed that it's not visible at && flag[0]? or did you mean `flag` will be copied to sub-threads in parallel streams? – 123-xyz Sep 07 '17 at 17:01
  • I think this will be fine for sequential streams, even with the new optimizations allowed in Java 9. I'm not so sure about non-volatile access to shared state in case of parallel streams - might be worth a separate question. – Hulk Sep 07 '17 at 19:40
  • Ok, since `boolean[]` is an Object, not primitive, no matter `flag[0]` will be set to true once, twice and more. when `allMatch` is returned, the value must be visible to main thread because main thread only comes after one of the sub-threads which have set value is done, the main thread and that sub-thread are not running concurrently. this is what i'm understanding. – 123-xyz Sep 07 '17 at 21:55
  • 1
    The main thread will also participate in the stream evaluation, so it *will* run concurrently to sub threads, however, it is correct that there is a *happens-before* relationship between the actions performed by the sub-tasks and the result return from `allMatch`. This does not apply when short-circuiting, but since this can only return earlier when returning `false`, this is not a problem here. So this code works, but if someone tries to adapt it to reuse it for another use case, it might break. – Holger Sep 08 '17 at 13:01
  • @Holger thank you for the explaining. I thought main thread doesn't participate the stream evaluation and only return result when all sub-tasks are done(or broken). but i do have a question about: ''main thread will also participate in the stream evaluation", why or how? ` Stream.of(1, 2, 3).parallel().allMatch(i -> { if (i % 2 == 0) { Thread.sleep(10000); } else { Thread.sleep(1000); } return i % 2 == 0; });` should i expect the result is returned in 1 second or 10 seconds? – 123-xyz Sep 08 '17 at 17:07
  • @Hoger, by the way, can you please also let me know what's 'short-circuiting'? does it mean the result can be returned by main thread before all the sub-tasks are done? and which stream operations support that? – 123-xyz Sep 08 '17 at 17:15
  • 2
    @123-xyz: the main thread will contribute, because otherwise, it’s an unused resource. Note, however, that the common pool has a default configuration to create “number of cores minus one” worker threads, so the total concurrency usually matches the number of cores. “short-circuiting” means that the operation might complete without processing all elements. It will still cancel pending sub-jobs and wait for their termination, but that’s not necessarily sufficient to establish a *happens-before* relationship of a a `peek` action of a sub-job whose result is not used and the main thread. – Holger Sep 11 '17 at 08:11
  • 2
    But as said, this is not a problem here. `allMatch` can only complete earlier if one element evaluates to `false`, so the `flag` is irrelevant, also the `peek` before evaluating to `false` will perform a visible write of `true` to the array and since all threads are only writing `true` to the array, there can’t be a contradicting write. If you want to know which operations may short-circuit, look at [the documentation](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html) and search for “short-circuiting”… – Holger Sep 11 '17 at 08:18
0

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

Lothar
  • 5,323
  • 1
  • 11
  • 27
  • That is basically the same as [this answer](https://stackoverflow.com/a/46083634/2513200), except for (ab-)using `filter()` or `allMatch()` for side effects, wich is arguably worse than just using `peek()` for this. – Hulk Sep 12 '17 at 08:47
-2
return !stream.filter(Whatever::someCheck).collect(Collectors.toList()).isEmpty()
daniu
  • 14,137
  • 4
  • 32
  • 53
  • 1
    Maybe `.count()` is better since you don't have to create a new list just to see if its' empty – Clayn Sep 06 '17 at 12:55
  • The problem with collecting into a collection is that it might cause an OOME (OutOfMemoryError) if the stream has many elements. – AlikElzin-kilaka Sep 06 '17 at 12:56
  • 3
    But the OP wants to know if all the elements match. Your code checks if any element matches. – Eran Sep 06 '17 at 13:00
  • @Eran I just realized that and deleted that portion of the answer. – daniu Sep 06 '17 at 13:01
  • 5
    This is just an inefficient way to express `.filter(Whatever::someCheck).count() > 0`, which is already an inefficient way of expressing `.anyMatch(Whatever::someCheck)`, which is not what the OP wants… – Holger Sep 06 '17 at 13:39