Java - Filtrer un flux avec les expressions Lambda PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

Java - Filtrer un flux avec des expressions Lambda

Les flux Java ont été introduits depuis Java 8 en 2014, dans le but d'introduire Java verbeux dans un paradigme de programmation fonctionnelle. Java Streams expose de nombreuses opérations fonctionnelles flexibles et puissantes pour effectuer le traitement des collections en une seule ligne.

Le filtrage des collections basé sur un prédicat reste l'une des opérations fonctionnelles les plus couramment utilisées et peut être effectué avec un Predicate ou plus concis - avec un Expression Lambda.

Dans ce petit guide, nous verrons comment vous pouvez filtrer un flux Java 8 avec des expressions Lambda.

Filtrage des flux en Java

En général, tout Stream peut être filtré via le filter() méthode, et un prédicat donné :

Stream filter(Predicate<? super T> predicate)

Chaque élément du flux est exécuté sur le prédicat et est ajouté au flux de sortie si le prédicat renvoie true. Vous pouvez fournir un Predicate exemple:

Predicate contains = s -> s.contains("_deprecated");
List results = stream.filter(contains).collect(Collectors.toList());

Ou simplifiez-le en fournissant une expression Lambda :

List results = stream.filter(s -> s.contains("_deprecated"))
                             .collect(Collectors.toList());

Ou même réduire l'expression Lambda en un référence de méthode:


List results = stream.filter(String::isEmpty)
                             .collect(Collectors.toList());

Avec les références de méthode, vous ne pouvez pas passer d'arguments, cependant, vous pouvez définir des méthodes dans l'objet que vous filtrez et les adapter pour qu'elles soient facilement filtrables (tant que la méthode n'accepte pas d'arguments et renvoie un boolean).

Rappelez-vous que les flux ne sont pas des collections – ce sont des flux de collections, et vous devrez les récupérer dans n'importe quelle collection telle qu'une List, Map, etc. pour leur donner une permanence. De plus, toutes les opérations effectuées sur les éléments de flux soit intermédiaire or terminal:

  • Les opérations intermédiaires renvoient un nouveau flux avec les modifications par rapport à l'opération précédente
  • Les opérations terminales renvoient un type de données et sont destinées à mettre fin à un pipeline de traitement sur un flux

filter() est un intermédiaire opération, et est destinée à être enchaînée avec d'autres opérations intermédiaires, avant que le flux ne soit terminé. Pour conserver toutes les modifications (telles que les modifications apportées aux éléments eux-mêmes ou aux résultats filtrés), vous devrez attribuer le résultat flux de sortie à une nouvelle variable de référence, via une opération terminale.

Remarque: Même lorsque vous enchaînez de nombreuses expressions lambda, vous ne rencontrerez peut-être pas de problèmes de lisibilité, avec des sauts de ligne appropriés.

Dans les exemples suivants, nous allons travailler avec cette liste de livres :

Book book1 = new Book("001", "Our Mathematical Universe", "Max Tegmark", 432, 2014);
Book book2 = new Book("002", "Life 3.0", "Max Tegmark", 280, 2017);
Book book3 = new Book("003", "Sapiens", "Yuval Noah Harari", 443, 2011);
        
List books = Arrays.asList(book1, book2, book3);

Filtrer la collection avec Stream.filter()

Filtrons cette collection de livres. N'importe quel prédicat est accepté - alors, par exemple, filtrons par quels livres ont plus de 400 pages :

List results = books.stream()
                          .filter(b -> b.getPageNumber() > 400)
                          .collect(Collectors.toList());

Cela donne une liste qui contient :

[
Book{id='001', name='Our Mathematical Universe', author='Max Tegmark', pageNumber=432, publishedYear=2014}, 
Book{id='003', name='Sapiens', author='Yuval Noah Harari', pageNumber=443, publishedYear=2011}
]

Lors du filtrage, une méthode très utile pour enchaîner est map(), qui vous permet de mapper des objets à une autre valeur. Par exemple, nous pouvons associer chaque livre à son nom, et ainsi renvoyer uniquement le noms des livres qui correspondent au prédicat de la filter() appel:

List results = books.stream()
                            .filter(b -> b.getPageNumber() > 400)
                            .map(Book::getName)
                            .collect(Collectors.toList());

Cela se traduit par une liste de chaînes :

[Our Mathematical Universe, Sapiens]

Filtrer la collection sur plusieurs prédicats avec Stream.filter()

Généralement, nous aimerions filtrer les collections selon plusieurs critères. Cela peut être fait en enchaînant plusieurs filter() en cours or en utilisant un prédicat de court-circuit, qui vérifie deux conditions en une seule filter() appel.

 List results = books.stream()
                    .filter(b -> b.getPageNumber() > 400 && b.getName().length() > 10)
                    .collect(Collectors.toList());
                    


 List results2 = books.stream()
                    .filter(b -> b.getPageNumber() > 400)
                    .filter(b -> b.getName().length() > 10)
                    .collect(Collectors.toList());

Consultez notre guide pratique et pratique pour apprendre Git, avec les meilleures pratiques, les normes acceptées par l'industrie et la feuille de triche incluse. Arrêtez de googler les commandes Git et en fait apprendre il!

Lorsque vous utilisez plusieurs critères, les appels lambda peuvent devenir un peu longs. À ce stade, les extraire en tant que prédicats autonomes pourrait offrir plus de clarté. Cependant, quelle approche est la plus rapide ?

Filtre unique avec condition complexe ou filtres multiples ?

Cela dépend de votre matériel, de la taille de votre collection et de l'utilisation ou non de flux parallèles. En général, un filtre avec une condition complexe surpassera plusieurs filtres avec des conditions plus simples (collections petites à moyennes) ou fonctionnera au même niveau (très grandes collections). Si vos conditions sont trop longues, il peut être avantageux de les répartir sur plusieurs filter() appels, pour une meilleure lisibilité, car les performances sont très similaires.

Le meilleur choix est d'essayer les deux, notez les performances sur le Dispositif cible, et ajustez votre stratégie en conséquence.

Utilisateur GitHub volkodav a fait un benchmark de filtrage dans les opérations de débit / s, et a hébergé les résultats sur le "javafilters-benchmarks" dépôt. Les résultats sont résumés dans un tableau informatif :

Il montre une nette diminution des rendements à des tailles de collecte plus importantes, les deux approches fonctionnant à peu près au même niveau. Les flux parallèles bénéficient de manière significative à des tailles de collection plus grandes, mais limitent les performances à des tailles plus petites (inférieures à ~ 10k éléments). Il convient de noter que les flux parallèles ont maintenu leur débit bien mieux que les flux non parallèles, ce qui les rend beaucoup plus robustes à l'entrée.

Horodatage:

Plus de Stackabuse