Search code examples
javajava-streamillegalstateexception

Why is IllegalStateException not thrown after calling two terminal operations on the same stream?


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));

Solution

  • 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