Zephyrnet-logotyp

Java – Filtrera en ström med Lambda-uttryck

Datum:

Java-strömmar har introducerats ända tillbaka i Java 8 2014, i ett försök att introducera verbose Java till ett funktionellt programmeringsparadigm. Java Streams exponerar många flexibla och kraftfulla funktionella operationer för att utföra insamlingsbearbetning i one-liners.

Filtrering av samlingar baserade på något predikat är fortfarande en av de mest använda funktionella operationerna och kan utföras med en Predicate eller mer kortfattat – med en Lambdauttryck.

I den här korta guiden tar vi en titt på hur du kan filtrera en Java 8 Stream med Lambda Expressions.

Filtrera strömmar i Java

I allmänhet, någon Stream kan filtreras via filter() metod och ett givet predikat:

Stream filter(Predicate<? super T> predicate)

Varje element i strömmen körs mot predikatet och läggs till utgångsflödet om predikatet återkommer true. Du kan leverera en Predicate exempel:

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

Eller förenkla det genom att tillhandahålla ett Lambda-uttryck:

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

Eller till och med kollapsa Lambda-uttrycket till ett metodreferens:


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

Med metodreferenser kan du inte skicka argument, men du kan definiera metoder i objektet du filtrerar och skräddarsy dem så att de är lätta att filtrera (så länge som metoden inte accepterar argument och returnerar en boolean).

Kom ihåg att strömmar är inte samlingar – de är bäckar av samlingar, och du måste samla tillbaka dem i valfri samling som en List, Mapetc. för att ge dem beständighet. Dessutom görs alla operationer på strömelement heller mellanliggande or terminala:

  • Mellanliggande operationer returnerar en ny ström med ändringar från föregående operation
  • Terminaloperationer returnerar en datatyp och är avsedda att avsluta en pipeline av bearbetning på en ström

filter() är en mellanliggande operation, och är avsedd att kedjas med andra mellanliggande operationer, innan strömmen avslutas. För att bevara ändringar (som ändringar av själva elementen eller filtrerade resultat), måste du tilldela den resulterande utgående ström till en ny referensvariabel, genom en terminaloperation.

Notera: Även när du kopplar ihop många lambda-uttryck kanske du inte stöter på läsbarhetsproblem, med korrekta radbrytningar.

I följande exempel kommer vi att arbeta med den här listan med böcker:

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

Filtrera samling med Stream.filter()

Låt oss filtrera den här samlingen av böcker. Vilket predikat som helst gäller – så låt oss till exempel filtrera efter vilka böcker som har över 400 sidor:

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

Detta resulterar i en lista som innehåller:

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

Vid filtrering är en riktigt användbar metod att kedja map(), som låter dig mappa objekt till ett annat värde. Till exempel kan vi mappa varje bok till dess namn, och därmed bara returnera namn av de böcker som passar predikatet från filter() ringa upp:

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

Detta resulterar i en lista med strängar:

[Our Mathematical Universe, Sapiens]

Filtersamling på flera predikat med Stream.filter()

Vanligtvis vill vi filtrera samlingar efter mer än ett kriterium. Detta kan göras genom att kedja flera filter() samtal or med hjälp av ett kortslutningspredikat, som cheks för två tillstånd i en enda filter() ring upp.

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

Kolla in vår praktiska, praktiska guide för att lära dig Git, med bästa praxis, branschaccepterade standarder och medföljande fuskblad. Sluta googla Git-kommandon och faktiskt lära Det!

När man använder flera kriterier – lambda-samtal kan bli något långdragna. Vid det här laget kan extrahering av dem som fristående predikat ge mer klarhet. Men vilket tillvägagångssätt är snabbare?

Ett filter med komplext tillstånd eller flera filter?

Det beror på din hårdvara, hur stor din samling är och om du använder parallella strömmar eller inte. I allmänhet – ett filter med ett komplext tillstånd kommer att överträffa flera filter med enklare villkor (små till medelstora samlingar), eller prestera på samma nivå (mycket stora samlingar). Om dina villkor är för långa – kan du tjäna på att fördela dem över flera filter() kräver förbättrad läsbarhet, eftersom prestandan är mycket lika.

Det bästa valet är att prova båda, notera prestandan på målenhet, och justera din strategi därefter.

GitHub-användare volkodavs gjorde ett filtreringsriktmärke i genomströmningsoperationer/er, och var värd för resultaten på "javafilter-benchmarks" förvaret. Resultaten sammanfattas i en informativ tabell:

Det visar en tydlig minskning av avkastningen vid större samlingsstorlekar, där båda metoderna presterar på samma nivå. Parallella strömmar gynnas avsevärt vid större samlingsstorlekar, men dämpar prestandan vid mindre storlekar (under ~10k element). Det är värt att notera att parallella strömmar bibehöll sin genomströmning mycket bättre än icke-parallella strömmar, vilket gjorde dem betydligt mer robusta för inmatning.

plats_img

Senaste intelligens

plats_img