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");
}
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 File
s, 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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With