I learned something today. Anyone able to guess wh...
# ask-ai
c
I learned something today. Anyone able to guess what this code snippet prints out? It's a stream flatmapped inside a stream flatmapped inside a stream. I'll put the answer in the thread, so don't open the thread if you want to puzzle about it first.
Copy code
public static void main(String[] args) {
    final Stream<Integer> stream = Stream.of(1)
        .peek(i -> System.out.println("outer stream"))
        .flatMap(i -> {
          return Stream.of(1)
              .peek(i1 -> System.out.println("middle stream"))
              .flatMap(i2 -> {
                return Stream.of(1).peek(i3 -> System.out.println("inner stream"))
                    .onClose(() -> System.out.println("inner stream closed"));
              })
              .onClose(() -> System.out.println("middle stream closed"));
        }).onClose(() -> System.out.println("outer stream closed"));

    System.out.println("collection is about to start");
    stream.collect(Collectors.toList());
    System.out.println("collection is done");
  }
answer:
Copy code
collection is about to start
outer stream
middle stream
inner stream
inner stream closed
middle stream closed
collection is done
the outer stream, while operated upon is never closed.
j
huh
so this is because it’s autoclosable but the stream isn’t in a closure that tries-with-resources?
c
it does mean if you need to guarantee a stream gets closed if it is fully consumed but don't trust the consumer to close it you can do something like this i think.
Copy code
thisStreamMayNotBeClosed = Stream.of(1).flatMap(i -> butThisStreamWillBeClosed);
j
so wrapping the whole stream assignment in a
try
works, which makes sense to me
c
yup.
j
it isn’t clear to me why the
close
is getting called in the flatmap case
it just kind of says
flatMap
does close streams for you
I must be missing something when looking for it while exploring code in
java.util.stream
oh no I’m just blind
under the hood the flatmap is taking the stream and doing a try with resources in
java.util.stream.ReferencePipeline.flatMap
🎉 1
cool
were we not closing something somewhere?
c
nah. nothing wrong yet anyway.
i was playing with how to handle onclose on some db queries in the jdbc source.
j
ah
c
and now i know i can force it, which is neat.
r
c
yes! definitely.
r
Interestingly, does it close streams that are 'from outside'? Let me try.
c
i have a case where the stream is created in a method but is consumed by some outside consumer. so i can't try-with-resource it, but i want a guarantee that when it has been consumed, on close is called.
contrived example:
r
Oh interesting, so when someone finishes consuming
thisStreamMayNotBeClosed
you will close
butThisStreamWillBeClosed
. That's a pretty clever use.
🎉 1
c
Copy code
public static class StreamMaker {
    public static Stream<Integer> makeStream() {
      return Stream.of(1).onClose(() -> System.out.println("close a vital resource");
    }
  }

  public static void main(String[] args) {
    // external caller fails to call close!
StreamMaker.makeStream().collect(Collectors.toList());
  }
but wrapping in the flatmap gives me the guarantee.
Copy code
public static class StreamMaker {
  public static Stream<Integer> makeStream() {
    return Stream.of(1).flatMap(i -> Stream.of(1).onClose(() -> System.out.println("close a vital resource"));
  }
}

public static void main(String[] args) {
  // external caller fails to call close, but it is okay because we are wrapped in a flatMap.
StreamMaker.makeStream().collect(Collectors.toList());
}
r
I was thinking to myself why all stream terminal operations do not cause a Stream#close since no stream is usable post-terminal-operation. After all: • All Streams are unusable post-terminal-operation • Non-IO-streams don't need the close so Stream#close is unnecessary there • IO-streams need the close in some cases and not others but that is a property of the underlying thing (e.g. a Files#lines obviously needs a File#close right after terminal, but a Connection#close shouldn't be called after you read all the rows) So the choice must be because it simplifies the common case in the API use.
👍 1
There's probably a mailing list somewhere where people are discussing this.
My personal confession is that I did not even know
Stream#close
existed.
c
legit. i think it's pretty common that people forget about
Stream#close
. i didn't really realize it existed until the last couple years.
j
post-terminal-operation
in the original example there is no clearly terminal operation, which is one difficulty
After today I’m pretty sure I’ve written some code that doesn’t close streams properly…
r
Stream#collect
is terminal, right?
c
yeah. you can't operate on the stream. anymore after that.
but it is not technically closed.
r
j
ooh
yeah
it is labelled as terminal
I guess something that implements
Stream
could be using an underlying resource for some other methods even if the stream has been terminated? Therefore it doesn’t make sense to autoclose?