I was given to understand that collect()
and forEach()
were both stream terminal operations and that calling them both on the same stream would throw an IllegalStateException
. However, the following code compiles successfully and prints the length of each String in ascending order. No exceptions are thrown. How can this be so?
List<String> list = Arrays.asList("ant", "bird", "chimpanzee", "dolphin");
list.stream().collect(Collectors.groupingBy(String::length))
.forEach((a, b) -> System.out.println(a));
The forEach
method that you are calling is not the Stream::forEach
method, but the Map::forEach
method, as you are calling it on the return value of collect(...)
, which is a Map
. A feature of the Map::forEach
method is that it takes a BiConsumer
, instead of a Consumer
. A stream's forEach
never takes a lambda with two arguments!
So you are only calling one terminal operation, namely collect
on the stream. After that point, you never did anything with the stream again (you started working with the returned Map
), which is why no IllegalStateExcepton
is thrown.
To actually call two terminal operations on the same stream, you need to put the stream into a variable first:
List<String> list = Arrays.asList("ant", "bird", "chimpanzee", "dolphin");
Stream<String> stream = list.stream(); // you need this extra variable.
stream.collect(Collectors.groupingBy(String::length));
stream.forEach((a) -> System.out.println(a)); // this will throw an exception, as expected