Java Language Utiliser des flux

Exemple

Un Stream est une séquence d'éléments sur laquelle des opérations d'agrégation séquentielles et parallèles peuvent être effectuées. Un Stream donné peut potentiellement contenir une quantité illimitée de données. En conséquence, les données reçues d'un Stream sont traitées individuellement à son arrivée, au lieu d'effectuer un traitement par lot sur les données. Lorsqu'elles sont combinées avec des expressions lambda, elles fournissent un moyen concis d'effectuer des opérations sur des séquences de données en utilisant une approche fonctionnelle.

Exemple: ( voir ça marche sur Ideone )

Stream<String> fruitStream = Stream.of("apple", "banana", "pear", "kiwi", "orange");

fruitStream.filter(s -> s.contains("a"))
           .map(String::toUpperCase)
           .sorted()
           .forEach(System.out::println);

Sortie:

POMME
BANANE
ORANGE
POIRE

Les opérations effectuées par le code ci-dessus peuvent être résumées comme suit:

  1. Créez un Stream<String> contenant un Stream ordonné d’éléments de String de caractères fruit à l’aide de la méthode de fabrique statique Stream.of(values) .

  2. L'opération filter() conserve uniquement les éléments correspondant à un prédicat donné (les éléments testés par le prédicat retournent true). Dans ce cas, il conserve les éléments contenant un "a" . Le prédicat est donné sous la forme d'une expression lambda .

  3. L'opération map() transforme chaque élément en utilisant une fonction donnée, appelée mappeur. Dans ce cas, chaque String Fruit est mappée sur sa version String majuscule à l'aide de la référence de méthode String::toUppercase .

    Notez que l'opération map() renverra un flux avec un type générique différent si la fonction de mappage renvoie un type différent de son paramètre d'entrée. Par exemple, sur un Stream<String> appel de .map(String::isEmpty) renvoie un Stream<Boolean>

  4. L'opération sort sorted() trie les éléments du Stream fonction de leur ordre naturel (lexicographiquement, dans le cas de la String ).

  5. Enfin, l'opération forEach(action) exécute une action qui agit sur chaque élément du Stream , en le transmettant à un consommateur . Dans l'exemple, chaque élément est simplement imprimé sur la console. Cette opération est une opération de terminal, ce qui rend son fonctionnement impossible.

    Notez que les opérations définies sur le Stream sont effectuées en raison du fonctionnement du terminal. Sans opération de terminal, le flux n'est pas traité. Les flux ne peuvent pas être réutilisés. Une fois qu'une opération de terminal est appelée, l'objet Stream devient inutilisable.

Opérations en chaîne

Les opérations (comme vu ci-dessus) sont enchaînées pour former ce qui peut être vu comme une requête sur les données.


Fermeture des cours d'eau

Notez qu'un Stream n'a généralement pas besoin d'être fermé. Il est seulement nécessaire de fermer les flux qui fonctionnent sur les canaux IO. La plupart des types de Stream ne fonctionnent pas sur les ressources et ne nécessitent donc pas de fermeture.

L'interface Stream étend AutoCloseable . Les flux peuvent être fermés en appelant la méthode close ou en utilisant des instructions try-with-resource.

Un exemple de cas d'utilisation où un Stream doit être fermé est lorsque vous créez un Stream de lignes à partir d'un fichier:

try (Stream<String> lines = Files.lines(Paths.get("somePath"))) {
    lines.forEach(System.out::println);
}

L'interface Stream déclare également la méthode Stream.onClose() qui vous permet d'enregistrer les gestionnaires Runnable qui seront appelés lorsque le flux sera fermé. Un exemple de cas d'utilisation est celui où le code qui produit un flux doit savoir quand il est utilisé pour effectuer un nettoyage.

public Stream<String>streamAndDelete(Path path) throws IOException {
    return Files.lines(path).onClose(() -> someClass.deletePath(path));
}

Le gestionnaire d'exécution ne s'exécutera que si la méthode close() est appelée, explicitement ou implicitement, par une instruction try-with-resources.


Commande en traitement

Le traitement d'un objet Stream peut être séquentiel ou parallèle .

En mode séquentiel , les éléments sont traités dans l'ordre de la source du Stream . Si le Stream est commandé (par exemple, une implémentation SortedMap ou une List ), le traitement est garanti pour correspondre à l'ordre de la source. Dans d'autres cas, toutefois, il convient de ne pas dépendre de la commande (voir: l'ordre d'itération keySet() Java HashMap keySet() cohérent? ).

Exemple:

List<Integer> integerList = Arrays.asList(0, 1, 2, 3, 42); 

// sequential 
long howManyOddNumbers = integerList.stream()
                                    .filter(e -> (e % 2) == 1)
                                    .count(); 

System.out.println(howManyOddNumbers); // Output: 2

Vivre sur Ideone

Le mode parallèle permet l'utilisation de plusieurs threads sur plusieurs cœurs, mais il n'y a aucune garantie sur l'ordre dans lequel les éléments sont traités.

Si plusieurs méthodes sont appelées sur un Stream séquentiel, toutes les méthodes ne doivent pas être appelées. Par exemple, si un Stream est filtré et que le nombre d'éléments est réduit à un, aucun appel ultérieur à une méthode telle que le sort ne se produira. Cela peut augmenter les performances d'un Stream séquentiel - une optimisation impossible avec un Stream parallèle.

Exemple:

// parallel
long howManyOddNumbersParallel = integerList.parallelStream()
                                            .filter(e -> (e % 2) == 1)
                                            .count();

System.out.println(howManyOddNumbersParallel); // Output: 2

Vivre sur Ideone


Différences par rapport aux conteneurs (ou aux collections )

Bien que certaines actions puissent être effectuées à la fois sur les conteneurs et les flux, elles servent en fin de compte à des objectifs différents et prennent en charge différentes opérations. Les conteneurs se concentrent davantage sur la manière dont les éléments sont stockés et sur la manière dont ces éléments peuvent être utilisés efficacement. Un Stream , d'autre part, ne fournit pas un accès et une manipulation directs à ses éléments; Il est plus dédié au groupe d'objets en tant qu'entité collective et effectue des opérations sur cette entité dans son ensemble. Stream et Collection sont des abstractions de haut niveau distinctes pour ces différents objectifs.