1

1) Why exactly second forEach() is not working? What is the exact rule?

2) How can I reset the stream to run forEach on it once more? Do I always need to create another instance of IntStream like IntStream sm2 = str.chars(); to work on sm2 (sm2.forEach(...)) or there is a way to "reset" sm ?

String str = "A B C D";
IntStream sm = str.chars();

sm.forEach(System.out::println); // works

// IllegalStateException: stream has already been operated upon or closed
sm.forEach(ch -> System.out.println(ch)); 

P.S. I know that forEach() is a terminal operation, but I understand it in a way that forEach() does not return the stream =>I cannot chain calls, that does not answer my question (I don't need to chain calls any further separating such calls with dots).

Pshemo
  • 122,468
  • 25
  • 185
  • 269
user10777718
  • 723
  • 4
  • 16
  • 1
    https://www.baeldung.com/java-stream-operated-upon-or-closed-exception – Ravindra Ranwala Mar 30 '19 at 09:49
  • Kind of duplicate of [https://stackoverflow.com/questions/23860533/copy-a-stream-to-avoid-stream-has-already-been-operated-upon-or-closed](https://stackoverflow.com/questions/23860533/copy-a-stream-to-avoid-stream-has-already-been-operated-upon-or-closed) (with an informative answer by Brian Goetz) – Ole V.V. Mar 30 '19 at 09:56
  • 1
    A *terminal operation* not only doesn’t return a stream, it also closes the stream you call it on. – Ole V.V. Mar 30 '19 at 09:58
  • 1
    *In Java 8, each Stream class represents a **single-use** sequence of data and supports several I/O operations.* – Vishwa Ratna Mar 30 '19 at 10:02

3 Answers3

1

The stream was already emptied by the first forEach loop. If you want to repeatedly use the forEach loop you have to copy the original stream or data. You could create another stream with the same data:

IntStream sm = str.chars();

sm.forEach(System.out::println); // works

sm = str.chars();

sm.forEach(ch -> System.out.println(ch)); // works
  • In your answer put **"Solution consists of creating a new Stream each time we need one."** not reusing the older one. – Vishwa Ratna Mar 30 '19 at 10:20
1

There is always a Stream::peek method, which does perform a Consumer operation and is non-terminal. However, as the docs state:

This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline

So I'm not sure if this is exactly right for you. You can read more here: In Java streams is peek really only for debugging?

So I would recommend to just execute whatever you want to inside the single forEach:

stream.forEach(e -> {
    operation1(e);
    operation2(e);
});

As to why forEach closes the stream - just because that's how the API was designed. :)

Nestor Sokil
  • 2,162
  • 12
  • 28
0

this is a core notion of a stream. a stream is a collection of data that may be infinite. As such, to be memory efficient, we cannot hold all elements in memory.

A stream can be seen as "one element + the way to get the next". So if you iterate over all the stream, you "don't know" how to iterate again, because that info was discarded.

if you need to iterate multiple times, then choose another collection (List?)

pedrorijo91
  • 7,635
  • 9
  • 44
  • 82