Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly close a variable amount of streams?

I'm creating multiple streams which I have to access in parallel (or possibly-parallel). I know how to make a try-with-resources when the amount of resources is fixed at compile-time, but what if the amount of resources is determined by a parameter?

I have something like this:

private static void foo(String path, String... files) throws IOException {
    @SuppressWarnings("unchecked")
    Stream<String>[] streams = new Stream[files.length];

    try {
        for (int i = 0; i < files.length; i++) {
            final String file = files[i];
            streams[i] = Files.lines(Paths.get(path, file))
                .onClose(() -> System.out.println("Closed " + file));
        }

        // do something with streams
        Stream.of(streams)
            .parallel()
            .flatMap(x -> x)
            .distinct()
            .sorted()
            .limit(10)
            .forEach(System.out::println);
    }
    finally {
        for (Stream<String> s : streams) {
            if (s != null) {
                s.close();
            }
        }
    }
}
like image 456
Mark Jeronimus Avatar asked May 07 '15 13:05

Mark Jeronimus


People also ask

Which method is used to close the stream?

close() method is used to close the writer. close() flushes and then closes the stream.

What is the right way to close the streams in Java?

The java. io. InputStream. close() method closes this stream and releases any system resources associated with the stream.

Why do we need to close streams?

yes you need to close stream because, the stream is already full with content and when you close the stream then you can use it again. also data is flush in drive when use flush method. when you close the stream JVM will see that stream is not can be use for further operation.

What happens if you dont close a stream?

Therefore, if we forget to close the stream, the underlying channel will remain open and then we would end up with a resource leak.


1 Answers

You could write a composite AutoCloseable for managing a dynamic amount of AutoCloseable:

import java.util.ArrayList;
import java.util.List;

public class CompositeAutoclosable<T extends AutoCloseable> implements AutoCloseable {
    private final List<T> components= new ArrayList<>();

    public void addComponent(T component) { components.add(component); }

    public List<T> getComponents() { return components; }

    @Override
    public void close() throws Exception {
        Exception e = null;
        for (T component : components) {
            try { component.close(); }
            catch (Exception closeException) {
                if (e == null) { e = closeException; }
                else { e.addSuppressed(closeException); }
            }
        }
        if (e != null) { throw e; }
    }
}

and you could use it in your method:

private static void foo(String path, String... files) throws Exception {
    try (CompositeAutoclosable<Stream<String>> streams 
            = new CompositeAutoclosable<Stream<String>>()) {
        for (int i = 0; i < files.length; i++) {
            final String file = files[i];
            streams.addComponent(Files.lines(Paths.get(path, file))
                .onClose(() -> System.out.println("Closed " + file)));
        }
        streams.getComponents().stream()
            .parallel()
            .flatMap(x -> x)
            .distinct()
            .sorted()
            .limit(10)
            .forEach(System.out::println);
    }
}
like image 160
gontard Avatar answered Oct 05 '22 11:10

gontard