Devrions-nous fermer un flux Java?

1. Vue d'ensemble

Avec l'introduction d'expressions lambda dans Java 8, il est possible d'écrire du code de manière plus concise et fonctionnelle. Les flux et les interfaces fonctionnelles sont au cœur de ce changement révolutionnaire de la plate-forme Java.

Dans ce rapide tutoriel, nous allons apprendre si nous devons fermer explicitement les flux Java 8 en les examinant du point de vue des ressources.

2. Fermeture des flux

Les flux Java 8 implémentent l' interface AutoCloseable :

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

En termes simples, nous devrions considérer les flux comme des ressources que nous pouvons emprunter et restituer lorsque nous en avons fini avec eux. Contrairement à la plupart des ressources, nous n'avons pas à toujours fermer les flux.

Cela peut sembler contre-intuitif au début, alors voyons quand nous devrions et quand nous ne devrions pas fermer les flux Java 8.

2.1. Collections, tableaux et générateurs

La plupart du temps, nous créons des instances Stream à partir de collections Java, de tableaux ou de fonctions de générateur. Par exemple, ici, nous opérons sur une collection de String via l'API Stream:

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

Parfois, nous générons un flux séquentiel fini ou infini:

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

De plus, nous pouvons également utiliser des flux basés sur des tableaux:

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

Lorsque vous traitez avec ces types de flux, nous ne devons pas les fermer explicitement. La seule ressource précieuse associée à ces flux est la mémoire, et Garbage Collection (GC) s'en charge automatiquement.

2.2. Ressources IO

Cependant, certains flux sont soutenus par des ressources IO telles que des fichiers ou des sockets. Par exemple, la méthode Files.lines () diffuse toutes les lignes du fichier donné:

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

Sous le capot, cette méthode ouvre une instance FileChannel , puis la ferme à la fermeture du flux. Par conséquent, si nous oublions de fermer le flux, le canal sous-jacent restera ouvert et nous nous retrouverions alors avec une fuite de ressources .

Pour éviter de telles fuites de ressources, il est fortement recommandé d'utiliser l' idiome try-with-resources pour fermer les flux basés sur les E / S:

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

De cette façon, le compilateur fermera automatiquement le canal. La clé à emporter ici est de fermer tous les flux basés sur IO .

Veuillez noter que la fermeture d'un flux déjà fermé lèverait IllegalStateException .

3. Conclusion

Dans ce court didacticiel, nous avons vu les différences entre les flux simples et les flux lourds d'E / S. Nous avons également appris comment ces différences influencent notre décision de fermer ou non les flux Java 8.

Comme d'habitude, l'exemple de code est disponible over sur GitHub.