More Than Five
More Than Five

Reputation: 10429

Caching Java 8 stream

Suppose I have a list which I perform multiple stream operations on.

  bobs = myList.stream()
        .filter(person -> person.getName().equals("Bob"))
        .collect(Collectors.toList())

...

and

  tonies = myList.stream()
        .filter(person -> person.getName().equals("tony"))
        .collect(Collectors.toList())

Can I not just do:

Stream<Person> stream = myList.stream();

which then means I can do:

  bobs = stream.filter(person -> person.getName().equals("Bob"))
        .collect(Collectors.toList())
  tonies = stream.filter(person -> person.getName().equals("tony"))
        .collect(Collectors.toList())

Upvotes: 3

Views: 2637

Answers (4)

Aman Chhabra
Aman Chhabra

Reputation: 3894

NO, you can't. One Stream can only be use one time It will throw below error when you will try to reuse:

java.lang.IllegalStateException: stream has already been operated upon or closed
       at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)

As per Java Docs:

A stream should be operated on (invoking an intermediate or terminal stream operation) only once.

But a neat solution to your query will be to use Stream Suplier. It looks like below:

Supplier<Stream<Person>> streamSupplier = myList::stream;
bobs = streamSupplier.get().filter(person -> person.getName().equals("Bob"))
        .collect(Collectors.toList())
tonies = streamSupplier.get().filter(person -> person.getName().equals("tony"))
        .collect(Collectors.toList())

But again, every get call will return a new stream.

Upvotes: 4

marsouf
marsouf

Reputation: 1147

Well, what you can do in your case is generate dynamic stream pipelines. Assuming that the only variable in your pipeline is the name of the person that you filter by.

We can represent this as a Function<String, Stream<Person>> as in the following :

final Function<String, Stream<Person>> pipelineGenerator = name -> persons.stream().filter(person -> Objects.equals(person.getName(), name));

final List<Person> bobs = pipelineGenerator.apply("bob").collect(Collectors.toList());

final List<Person> tonies = pipelineGenerator.apply("tony").collect(Collectors.toList());

Upvotes: 1

Jean-Baptiste Yun&#232;s
Jean-Baptiste Yun&#232;s

Reputation: 36431

No you can't, doc says:

A stream should be operated on (invoking an intermediate or terminal stream operation) only once.

But you can use a single stream by filtering all elements you want once and then group them the way you need:

Set<String> names = ...; // construct a sets containing bob, tony, etc
Map<String,List<Person>> r = myList.stream()
                                   .filter(p -> names.contains(p.getName())
                                   .collect(Collectors.groupingBy(Person::getName);
List<Person> tonies = r.get("tony");
List<Person> bobs = r.get("bob");

Upvotes: 1

Ousmane D.
Ousmane D.

Reputation: 56453

As already mentioned a given stream should be operated upon only once.

I can understand the "idea" of caching a reference to an object if you're going to refer to it more than once, or to simply avoid creating more objects than necessary.

However, you should not be concerned when invoking myList.stream() every time you need to query again as creating a stream, in general, is a cheap operation.

Upvotes: 0

Related Questions