Java — filtruj strumień za pomocą wyrażeń lambda PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Java – filtruj strumień za pomocą wyrażeń lambda

Strumienie Java zostały wprowadzone już w Javie 8 w 2014 roku, w celu wprowadzenia pełnej wersji Java do paradygmatu programowania funkcjonalnego. Java Streams udostępnia wiele elastycznych i wydajnych operacji funkcjonalnych do wykonywania przetwarzania kolekcji w jednym wierszu.

Filtrowanie kolekcji na podstawie jakiegoś predykatu pozostaje jedną z najczęściej używanych operacji funkcjonalnych i może być wykonywane za pomocą Predicate lub bardziej zwięźle – z Wyrażenie lambda.

W tym krótkim przewodniku przyjrzymy się, jak filtrować strumień Java 8 za pomocą wyrażeń lambda.

Filtrowanie strumieni w Javie

Ogólnie rzecz biorąc, każdy Stream można filtrować za pomocą filter() metody i danego predykatu:

Stream filter(Predicate<? super T> predicate)

Każdy element w strumieniu jest uruchamiany względem predykatu i jest dodawany do strumienia wyjściowego, jeśli predykat zwraca true. Możesz dostarczyć Predicate instancja:

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

Lub uprość to, podając wyrażenie lambda:

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

Lub nawet zwinąć wyrażenie lambda na odniesienie do metody:


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

W przypadku odwołań do metod nie można przekazywać argumentów, można jednak zdefiniować metody w filtrowanym obiekcie i dostosować je tak, aby były łatwe do filtrowania (o ile metoda nie akceptuje argumentów i zwraca boolean).

Pamiętaj, że strumienie nie są kolekcjami – to strumienie kolekcjii będziesz musiał zebrać je z powrotem do dowolnej kolekcji, takiej jak List, Mapitp., aby zapewnić im trwałość. Dodatkowo wszystkie operacje wykonywane na elementach strumieniowych pośredni or terminal:

  • Operacje pośrednie zwracają nowy strumień ze zmianami z poprzedniej operacji
  • Operacje terminalowe zwracają typ danych i mają na celu zakończenie potoku przetwarzania w strumieniu

filter() jest pośredni operacji i ma być połączony z innymi operacjami pośrednimi, zanim strumień zostanie zakończony. Aby zachować jakiekolwiek zmiany (takie jak zmiany samych elementów lub filtrowane wyniki), musisz przypisać wynikowy strumień wyjściowy do nowej zmiennej referencyjnej poprzez operację terminala.

Uwaga: Nawet w przypadku łączenia wielu wyrażeń lambda można nie napotkać problemów z czytelnością, z prawidłowymi podziałami wierszy.

W poniższych przykładach będziemy pracować z tą listą książek:

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

Filtruj kolekcję za pomocą Stream.filter()

Przefiltrujmy ten zbiór książek. Każdy predykat idzie – więc na przykład przefiltrujmy, według których książki mają ponad 400 stron:

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

Daje to listę, która zawiera:

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

Podczas filtrowania naprawdę przydatną metodą łączenia jest map(), który umożliwia mapowanie obiektów na inną wartość. Na przykład możemy zmapować każdą książkę na jej nazwę i w ten sposób zwrócić tylko Nazwy książek, które pasują do orzeczenia z filter() połączenie:

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

Daje to listę ciągów:

[Our Mathematical Universe, Sapiens]

Filtruj kolekcję na wielu predykatach za pomocą Stream.filter()

Zwykle chcemy filtrować kolekcje według więcej niż jednego kryterium. Można to zrobić, łącząc wiele filter() Połączenia or za pomocą predykatu zwarcia, który sprawdza w jednym przypadku dwa stany filter() połączenie.

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

Zapoznaj się z naszym praktycznym, praktycznym przewodnikiem dotyczącym nauki Git, zawierającym najlepsze praktyki, standardy przyjęte w branży i dołączoną ściągawkę. Zatrzymaj polecenia Google Git, a właściwie uczyć się to!

W przypadku korzystania z wielu kryteriów — wywołania lambda mogą być nieco dłuższe. W tym momencie wyodrębnienie ich jako samodzielnych predykatów może zapewnić większą przejrzystość. Ale które podejście jest szybsze?

Pojedynczy filtr o złożonym warunku czy wiele filtrów?

To zależy od twojego sprzętu, jak duża jest twoja kolekcja i czy używasz równoległych strumieni, czy nie. Ogólnie rzecz biorąc – jeden filtr ze złożonym warunkiem przewyższa wiele filtrów o prostszych warunkach (małe i średnie kolekcje) lub działa na tym samym poziomie (bardzo duże kolekcje). Jeśli twoje warunki są zbyt długie – możesz skorzystać z rozłożenia ich na wiele filter() wzywa do poprawy czytelności, ponieważ wydajność jest bardzo podobna.

Najlepszym wyborem jest wypróbowanie obu, zwróć uwagę na wydajność Urządzenie docelowei odpowiednio dostosuj swoją strategię.

Użytkownik GitHub Volkodaws wykonał test porównawczy filtrowania w operacjach przepustowości i udostępnił wyniki na „wskaźniki filtrów java” magazyn. Wyniki podsumowano w tabeli informacyjnej:

Pokazuje wyraźny spadek zwrotów przy większych rozmiarach kolekcji, przy czym oba podejścia działają na tym samym poziomie. Strumienie równoległe przynoszą znaczne korzyści przy większych rozmiarach kolekcji, ale ograniczają wydajność przy mniejszych rozmiarach (poniżej ~10 tys. elementów). Warto zauważyć, że równoległe strumienie utrzymywały swoją przepustowość znacznie lepiej niż strumienie nierównoległe, dzięki czemu są znacznie bardziej odporne na dane wejściowe.

Znak czasu:

Więcej z Nadużycie stosu