Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the diff between declaring a Stream with try-with-resources statement or not?

In Java 8, Stream (which is AutoCloseable) cannot be reused, once it is consumed or used, the stream will be closed. So what is the utility to declared with try-with-resources statement ?

Example with try-with-resources statement :

    public static void main(String[] args) throws IOException {

    try (Stream<Path> entries
            = Files.walk(Paths.get("."), 4, FileVisitOption.FOLLOW_LINKS)) {
        entries.forEach(x -> System.out.println(x.toAbsolutePath()));// the entries stream will be automatically closed at this point
        //..
        System.out.println("Still in the Try Block");
    } //The entries will be closed again because it is declared in the try-with-resources statement
}

And here the same example without the try catch block

public static void main(String[] args) throws IOException {
        Stream<Path> entries = Files.walk(Paths.get("."), 4, FileVisitOption.FOLLOW_LINKS);
        entries.forEach(x -> System.out.println(x.toAbsolutePath()));// the entries stream will be automatically closed at this point
        System.out.println("Is there a risk of resources leak ?");
    }

Which one is safer ?

After some answers I update my code to check if the stream has been closed or not:

Here the new code :

public static void main(String[] args) throws IOException {

    resourceWithTry();
    resourceWithoutTry();
}

private static void resourceWithTry() throws IOException {
    try (Stream<Path> entries
            = Files.walk(Paths.get("."), 4, FileVisitOption.FOLLOW_LINKS).onClose(() -> System.out.println("The Stream is closed"))) {
        entries.forEach(x -> System.out.println(x.toAbsolutePath()));// the entries stream will be not automatically closed at this point
        System.out.println("Still in the Try Block");
    } //The entries will be closed again because it is declared in the try-with-resources statement
}

private static void resourceWithoutTry() throws IOException {
    Stream<Path> entries
            = Files.walk(Paths.get("."), 4, FileVisitOption.FOLLOW_LINKS).onClose(() -> System.out.println("Without Try: The Stream is closed"));
    entries.forEach(x -> System.out.println(x.toAbsolutePath()));// the entries stream will be not automatically closed at this point
    System.out.println("Still in the Try Block");
}
like image 340
Aguid Avatar asked Nov 04 '17 06:11

Aguid


3 Answers

In Java 8, Stream (which is AutoCloseable) cannot be reused, once it is consumed or used, the stream will be closed.

Not exactly.
Stream terminal operations such as forEach() don't close the stream.
It makes the stream pipeline not consumable any longer.
Which is different.

Package java.util.stream description states :

After the terminal operation is performed, the stream pipeline is considered consumed, and can no longer be used; if you need to traverse the same data source again, you must return to the data source to get a new stream.

It doesn't say that the stream is closed.

So in the following code, the AutoCloseable.close() method of the Stream instance is never invoked :

Stream<Path> entries = Files.walk(Paths.get("."), 4, FileVisitOption.FOLLOW_LINKS);
entries.forEach(x -> System.out.println(x.toAbsolutePath()));

So is it always required to close the stream ? (with explicit close() invocation or better try-with-resources)

java.util.stream.Stream<T> javadocs explains that nearly all Stream instances don't need to be closed with AutoCloseable.close() after using.
It is only true for IO stream.

Streams have a close() method and implement AutoCloseable, but nearly all stream instances do not actually need to be closed after use. Generally, only streams whose source is an IO channel (such as those returned by Files.lines(Path, Charset)) will require closing. Most streams are backed by collections, arrays, or generating functions, which require no special resource management. (If a stream does require closing, it can be declared as a resource in a try-with-resources statement.)

In our case, you manipulate Files, so it makes sense to close the channel.
By using a try-with-resources, you avoid resource exhaustion exceptions and errors that may occur.
If any error or exception is risen during the processing, the resource may be all the same released.

like image 176
davidxxx Avatar answered Oct 29 '22 15:10

davidxxx


The stream processing may get interrupted, and then it will not be fully consumed and it will not get automatically closed. Consider this example:

Stream<Integer> nums = Stream.of(1, 2, 3);
nums.onClose(() -> System.out.println("close was called"));
nums.forEach(x -> { if (x > 1) throw new IllegalStateException(); });

This will crash in the middle, and the onClose will not get called. The stream remains open and you must close it manually.

If you use try-with-resources it will get correctly closed, no matter what, as you can observe:

try (Stream<Integer> nums = Stream.of(1, 2, 3)) {
    nums.onClose(() -> System.out.println("close was called"));
    nums.forEach(x -> { if (x > 1) throw new IllegalStateException(); });
}

This is a tortured example, since it doesn't consume any resources that would need to be closed, the garbage collectors will clean up, even though the stream was not closed.

So what is the utility to declared with try-with-resources statement ?

It ensures that the stream gets closed no matter what. When the stream is not backed by resources that would need to get closed, it doesn't actually matter.

The example above was just to demonstrate the difference, but in such code it's unnecessary paranoia to use try-with-resources, just noise in the code, so it's actually better without the try-with-resources.

like image 41
janos Avatar answered Oct 29 '22 17:10

janos


According to the docs, yes it is recommended (and safer) to close the stream returned in this instance using try-with-resources:

If timely disposal of file system resources is required, the try-with-resources construct should be used to ensure that the stream's close method is invoked after the stream operations are completed.

In other cases, where the Stream is fed from an array or a list, it makes no difference.

like image 36
Vasan Avatar answered Oct 29 '22 15:10

Vasan