Java: filtra un flusso con espressioni Lambda PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Java: filtra un flusso con espressioni Lambda

Java Streams è stato introdotto in Java 8 nel 2014, nel tentativo di introdurre Java dettagliato in un paradigma di programmazione funzionale. Java Streams espone molte operazioni funzionali flessibili e potenti per eseguire l'elaborazione della raccolta in una sola riga.

Il filtraggio delle raccolte in base ad alcuni predicati rimane una delle operazioni funzionali più comunemente utilizzate e può essere eseguito con a Predicate o più concisamente – con a Espressione lambda.

In questa breve guida daremo un'occhiata a come filtrare un flusso Java 8 con le espressioni Lambda.

Filtraggio dei flussi in Java

In generale, qualsiasi Stream può essere filtrato tramite il filter() metodo e un dato predicato:

Stream filter(Predicate<? super T> predicate)

Ogni elemento nel flusso viene eseguito rispetto al predicato e viene aggiunto al flusso di output se il predicato restituisce un risultato true. Puoi fornire a Predicate esempio:

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

Oppure semplificalo fornendo un'espressione Lambda:

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

O addirittura comprimere l'espressione Lambda in a riferimento al metodo:


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

Con i riferimenti ai metodi, non puoi passare argomenti, tuttavia, puoi definire metodi nell'oggetto che stai filtrando e adattarli per essere facilmente filtrabili (purché il metodo non accetti argomenti e restituisca un boolean).

Ricorda che gli stream non sono raccolte – sono flussi delle collezionie dovrai raccoglierli nuovamente in qualsiasi raccolta come a List, Map, ecc. per dare loro la permanenza. Inoltre, tutte le operazioni eseguite sugli elementi dello stream intermedio or terminale:

  • Le operazioni intermedie restituiscono un nuovo flusso con le modifiche rispetto all'operazione precedente
  • Le operazioni terminali restituiscono un tipo di dati e hanno lo scopo di terminare una pipeline di elaborazione su un flusso

filter() offre intermedio operazione ed è pensato per essere concatenato con altre operazioni intermedie, prima che il flusso venga terminato. Per rendere persistenti eventuali modifiche (come modifiche agli elementi stessi o risultati filtrati), dovrai assegnare il risultato flusso di uscita ad una nuova variabile di riferimento, attraverso un'operazione terminale.

Nota: Anche quando si concatenano molte espressioni lambda, potresti non incorrere in problemi di leggibilità, con interruzioni di riga adeguate.

Negli esempi seguenti, lavoreremo con questo elenco di libri:

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);

Filtra la raccolta con Stream.filter()

Filtriamo questa raccolta di libri. Va bene qualsiasi predicato, quindi filtriamo ad esempio in base a quali libri hanno più di 400 pagine:

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

Ciò si traduce in un elenco che contiene:

[
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}
]

Quando si filtra, un metodo davvero utile per concatenare è map(), che consente di mappare gli oggetti su un altro valore. Ad esempio, possiamo associare ogni libro al suo nome e quindi restituire solo il file nomi dei libri che si adattano al predicato del filter() chiamata:

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

Ciò si traduce in un elenco di stringhe:

[Our Mathematical Universe, Sapiens]

Filtra la raccolta su più predicati con Stream.filter()

Di solito, vorremmo filtrare le raccolte in base a più di un criterio. Questo può essere fatto concatenando più filter() chiamate or utilizzando un predicato di cortocircuito, che verifica due condizioni in una sola filter() chiamata.

 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());

Dai un'occhiata alla nostra guida pratica e pratica per l'apprendimento di Git, con le migliori pratiche, gli standard accettati dal settore e il cheat sheet incluso. Smetti di cercare su Google i comandi Git e in realtà imparare esso!

Quando si utilizzano più criteri, le chiamate lambda possono diventare piuttosto lunghe. A questo punto, estrarli come predicati autonomi potrebbe offrire maggiore chiarezza. Tuttavia, quale approccio è più veloce?

Filtro singolo con condizioni complesse o filtri multipli?

Dipende dal tuo hardware, da quanto è grande la tua raccolta e se utilizzi flussi paralleli o meno. In generale, un filtro con una condizione complessa avrà prestazioni migliori di più filtri con condizioni più semplici (raccolte da piccole a medie) o funzionerà allo stesso livello (raccolte molto grandi). Se le tue condizioni sono troppo lunghe, potresti trarre vantaggio dalla loro distribuzione su più livelli filter() richiede una migliore leggibilità, poiché le prestazioni sono molto simili.

La scelta migliore è provarli entrambi, notare le prestazioni sul dispositivo di destinazionee adattare la tua strategia di conseguenza.

Utente GitHub volkodavs ha eseguito un benchmark del filtraggio nelle operazioni/i di throughput e ha ospitato i risultati su “benchmark dei filtri Java” deposito. I risultati sono riassunti in una tabella informativa:

Mostra una chiara diminuzione dei rendimenti per raccolte di dimensioni maggiori, con entrambi gli approcci che si comportano all’incirca allo stesso livello. I flussi paralleli traggono notevoli vantaggi con raccolte di dimensioni maggiori, ma riducono le prestazioni con dimensioni più piccole (sotto i ~10 elementi). Vale la pena notare che i flussi paralleli mantengono il loro throughput molto meglio dei flussi non paralleli, rendendoli significativamente più robusti all'input.

Timestamp:

Di più da Impilamento