Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if java stream has been consumed

How can I check if a stream instance has been consumed or not (meaning having called a terminal operation such that any further call to a terminal operation may fail with IllegalStateException: stream has already been operated upon or closed.?

Ideally I want a method that does not consume the stream if it has not yet been consumed, and that returns a boolean false if the stream has been consumed without catching an IllegalStateException from a stream method (because using Exceptions for control flow is expensive and error prone, in particular when using standard Exceptions).

A method similar to hasNext() in Iterator in the exception throwing and boolean return behavior (though without the contract to next()).

Example:

public void consume(java.util.function.Consumer<Stream<?>> consumer, Stream<?> stream) {
   consumer.accept(stream);
   // defensive programming, check state
   if (...) {
       throw new IllegalStateException("consumer must call terminal operation on stream");
   }
}

The goal is to fail early if client code calls this method without consuming the stream.

It seems there is no method to do that and I'd have to add a try-catch block calling any terminal operation like iterator(), catch an exception and throw a new one.

An acceptable answer can also be "No solution exists" with a good justification of why the specification could not add such a method (if a good justification exists). It seems that the JDK streams usually have this snippets at the start of their terminal methods:

// in AbstractPipeline.java
if (linkedOrConsumed)
    throw new IllegalStateException(MSG_STREAM_LINKED);

So for those streams, an implementation of such a method would not seem that difficult.

like image 463
tkruse Avatar asked Jul 08 '19 01:07

tkruse


Video Answer


1 Answers

Taking into consideration that spliterator (for example) is a terminal operation, you can simply create a method like:

private static <T> Optional<Stream<T>> isConsumed(Stream<T> stream) {

    Spliterator<T> spliterator;
    try {
        spliterator = stream.spliterator();
    } catch (IllegalStateException ise) {
        return Optional.empty();
    }

    return Optional.of(StreamSupport.stream(
        () -> spliterator,
        spliterator.characteristics(),
        stream.isParallel()));
}

I don't know of a better way to do it... And usage would be:

Stream<Integer> ints = Stream.of(1, 2, 3, 4)
                                 .filter(x -> x < 3);

YourClass.isConsumed(ints)
         .ifPresent(x -> x.forEachOrdered(System.out::println));

Since I don't think there is a practical reason to return an already consumed Stream, I am returning Optional.empty() instead.

like image 62
Eugene Avatar answered Oct 30 '22 12:10

Eugene