Dovremmo chiudere un Java Stream?

1. Panoramica

Con l'introduzione delle espressioni lambda in Java 8, è possibile scrivere codice in modo più conciso e funzionale. Gli stream e le interfacce funzionali sono il cuore di questo cambiamento rivoluzionario nella piattaforma Java.

In questo breve tutorial, impareremo se dobbiamo chiudere esplicitamente i flussi Java 8 guardandoli dal punto di vista delle risorse.

2. Chiusura dei flussi

I flussi Java 8 implementano l' interfaccia AutoCloseable :

public interface Stream extends BaseStream { // omitted } public interface BaseStream extends AutoCloseable { // omitted }

In parole povere, dovremmo pensare ai flussi come risorse che possiamo prendere in prestito e restituire quando abbiamo finito con loro. A differenza della maggior parte delle risorse, non dobbiamo sempre chiudere i flussi.

All'inizio può sembrare controintuitivo, quindi vediamo quando dovremmo e quando non dovremmo chiudere gli stream Java 8.

2.1. Raccolte, array e generatori

La maggior parte delle volte, creiamo istanze Stream da raccolte Java, array o funzioni di generatore. Ad esempio, qui stiamo operando su una raccolta di String tramite l'API Stream:

List colors = List.of("Red", "Blue", "Green") .stream() .filter(c -> c.length() > 4) .map(String::toUpperCase) .collect(Collectors.toList());

A volte, stiamo generando un flusso sequenziale finito o infinito:

Random random = new Random(); random.ints().takeWhile(i -> i < 1000).forEach(System.out::println);

Inoltre, possiamo anche utilizzare flussi basati su array:

String[] colors = {"Red", "Blue", "Green"}; Arrays.stream(colors).map(String::toUpperCase).toArray()

Quando si ha a che fare con questi tipi di flussi, non dovremmo chiuderli esplicitamente. L'unica risorsa preziosa associata a questi flussi è la memoria e Garbage Collection (GC) se ne occupa automaticamente.

2.2. Risorse IO

Alcuni flussi, tuttavia, sono supportati da risorse di I / O come file o socket. Ad esempio, il metodo Files.lines () trasmette tutte le righe per il file specificato:

Files.lines(Paths.get("/path/to/file")) .flatMap(line -> Arrays.stream(line.split(","))) // omitted

Dietro le quinte, questo metodo apre un'istanza FileChannel e quindi la chiude alla chiusura del flusso. Pertanto, se dimentichiamo di chiudere il flusso, il canale sottostante rimarrà aperto e quindi si finirebbe con una perdita di risorse .

Per prevenire tali fughe di risorse, si consiglia vivamente di utilizzare l' idioma try-with-resources per chiudere i flussi basati su IO:

try (Stream lines = Files.lines(Paths.get("/path/to/file"))) { lines.flatMap(line -> Arrays.stream(line.split(","))) // omitted }

In questo modo, il compilatore chiuderà automaticamente il canale. Il punto chiave qui è chiudere tutti i flussi basati su IO .

Tieni presente che la chiusura di un flusso già chiuso genererebbe IllegalStateException .

3. Conclusione

In questo breve tutorial, abbiamo visto le differenze tra flussi semplici e flussi pesanti di IO. Abbiamo anche appreso come queste differenze influenzino la nostra decisione se chiudere o meno i flussi Java 8.

Come al solito, il codice di esempio è disponibile su GitHub.