Matthias Braun
Matthias Braun

Reputation: 34313

anyMatch inside allMatch

I have a couple of predicates that I all want to be satisfied.

The things that can satisfy those predicates are a handful of strings. An individual string doesn't have to satisfy all (or any) of those predicates, but after I've looked at the last string, all of the predicates have to be satisified.

My first take to represent this problem in Java was to use Stream's allMatch and anyMatch since I want all of the predicates to match any of the things to test:

Stream<String> thingsToTest = Stream.of("Hi", "predicates!", "oddball");

Predicate<String> startsWithH = string -> string.startsWith("H");
Predicate<String> endsWithBang = string -> string.endsWith("!");
Stream<Predicate<String>> predicates = Stream.of(startsWithH, endsWithBang);

// All of the strings have the chance to satisfy any predicate
boolean predicatesSatisfied = predicates.allMatch(pred -> thingsToTest.anyMatch(pred::test));

// I expect this to print "true"
System.out.println(predicatesSatisfied);

Sadly, this doesn't work but terminates with an IllegalStateException, telling me that the stream has already been operated upon or closed, which shouldn't come as a big surprise since for each predicate I give the strings a new chance to satisfy the predicate, using the string stream over and over. And streams are not meant to be reused for good reasons.

So how do I avoid this exception? Is there a more elegant alternative to anyMatch or allMatch?

Upvotes: 1

Views: 1456

Answers (2)

Tagir Valeev
Tagir Valeev

Reputation: 100199

When you need to use the Stream several times, the common solution is to create a Supplier<Stream> instead:

Supplier<Stream<String>> thingsToTest = () -> Stream.of("Hi", "predicates!", "oddball");

....

boolean predicatesSatisfied = predicates.allMatch(
                                    pred -> thingsToTest.get().anyMatch(pred::test));

Unlike @MatthiasBraun suggestion, using the Supplier it's not always necessary to actually store all the stream elements in the verbatim collection. For example, such thing is possible:

Supplier<Stream<String>> thingsToTest = 
             () -> IntStream.range(0, 10000).mapToObj(String::valueOf);

You just have to care that supplier always returns the same stream elements.

If you already have a collection, then you can create a supplier as well:

List<String> list = Arrays.asList("Hi", "predicates!", "oddball");
Supplier<Stream<String>> thingsToTest = list::stream;

Upvotes: 1

Matthias Braun
Matthias Braun

Reputation: 34313

To get around the IllegalStateException I use a List of strings and call its stream() method:

// Use List instead of Stream
List<String> thingsToTest = Arrays.asList("Hi", "predicates!", "oddball");

// Same old
Predicate<String> startsWithH = string -> string.startsWith("H");
Predicate<String> endsWithBang = string -> string.endsWith("!");
Stream<Predicate<String>> predicates = Stream.of(startsWithH, endsWithBang);

// Call stream() on the List
boolean predicatesSatisfied = predicates.allMatch(pred -> thingsToTest.stream().
                                                  anyMatch(pred::test));

Although this works fine, I'm not sure if it is the most elegant way to do this, so if you have a better idea, please go ahead and post your code or suggestion.

Upvotes: 3

Related Questions