Kod

Java Stream API: Przykłady użycia

Java Stream API: Przykłady użycia

Dowiedz się: Zawód programisty Java

Dowiedz się więcej

W tym artykule skupimy się na praktycznym zastosowaniu strumieni, unikając zbędnej teorii. Omówimy siedem typowych scenariuszy, w których użycie strumieni przynosi znaczące korzyści. Porównamy je również z tradycyjnymi podejściami imperatywnymi, aby jasno zademonstrować skuteczność strumieni w różnych sytuacjach.

Interfejs API strumieni – co to jest?

Ta metoda pozwala na efektywną interakcję ze strukturami danych w Javie, zwłaszcza kolekcjami, przy użyciu zasad programowania funkcyjnego. Podejście funkcyjne promuje czystszy i bardziej zrozumiały kod, upraszczając manipulację danymi i zwiększając czytelność programu. Programowanie funkcyjne i użycie wyrażeń lambda w Javie stały się ważnymi aspektami współczesnego programowania. Koncepcje te pozwalają na tworzenie bardziej zwięzłego i ekspresyjnego kodu, poprawiając czytelność i łatwość utrzymania aplikacji. W tym artykule omówimy podstawowe zasady programowania funkcyjnego i ich implementację w Javie, w tym przykłady wyrażeń lambda. Dowiedz się, jak te narzędzia mogą znacznie uprościć Twoją pracę i poprawić wydajność tworzenia oprogramowania. Czytaj dalej, aby zanurzyć się w świat programowania funkcyjnego i opanować lambdy w Javie. Strumień to narzędzie do przetwarzania danych ogólnego przeznaczenia. Nie jest to nowa struktura danych, lecz mechanizm wykorzystujący istniejące kolekcje do wyodrębniania nowych elementów. Strumienie umożliwiają wydajne przetwarzanie i transformację danych, upraszczając przetwarzanie dużych ilości informacji. Takie podejście pozwala programistom optymalizować kod, poprawiając jego czytelność i wydajność. Strumienie zyskują na popularności we współczesnych językach programowania, takich jak Java i Python, co czyni je niezbędnym elementem pracy z danymi.

Różne metody dostępne w interfejsie Stream służą do przetwarzania danych. Metody te umożliwiają standardowe operacje na kolekcjach, takie jak sortowanie, reorganizacja i filtrowanie. Później omówimy szczegółowo niektóre z tych metod.

Pomyśl o strumieniu jako o ciągłym strumieniu danych, a o łańcuchu wywołań metod jako o potoku, który przetwarza te dane. Takie podejście pozwala na efektywne zarządzanie przetwarzaniem danych, zapewniając szybki i zorganizowany proces.

Każda metoda pośrednia otrzymuje jako dane wejściowe wynik poprzedniego kroku, reprezentowany jako strumień. Wykonuje ona określoną część pracy i zwraca nowy strumień. Takie podejście umożliwia sekwencyjne przetwarzanie danych, zapewniając modułowość i łatwość obsługi podczas pracy ze strumieniami danych.

Ostateczna (terminalna) metoda może zwrócić wartość void lub zwrócić wynik inny niż typ strumienia.

Strumienie pozwalają programistom uniknąć pisania powtarzalnego kodu podczas pracy z kolekcjami. Korzystając ze strumieni, programiści mogą skupić się na logice aplikacji bez zagłębiania się w szczegóły implementacji. To znacznie upraszcza przetwarzanie danych i poprawia czytelność kodu. Strumienie optymalizują pracę z zestawami elementów, umożliwiając bardziej wydajne i deklaratywne wykonywanie operacji.

Istnieją również inne zalety:

  • Strumienie wspierają jedną z fundamentalnych zasad dobrego projektowania – niskie sprzężenie. Im mniej klasa wie o innych klasach, tym lepiej. Algorytm sortowania nie powinien przejmować się tym, co dokładnie sortuje. Właśnie to robią strumienie.
  • Dzięki strumieniom operacje na kolekcjach są łatwiejsze do zrównoleglenia: w podejściu imperatywnym wymagałoby to co najmniej jednego dodatkowego cyklu.
  • Strumienie zmniejszają liczbę efektów ubocznych: metody API strumieni nie modyfikują kolekcji źródłowych.
  • Dzięki API strumieni złożone algorytmy przetwarzania danych są zwięźle napisane.

Skoro już prawie dostrzegasz korzyści płynące ze strumieniowania, przejdźmy do aspektów praktycznych. Strumienie to potężne narzędzie do interakcji z odbiorcami i tworzenia unikalnych treści. Pozwalają one nie tylko dzielić się wiedzą i doświadczeniem, ale także budować społeczność wokół marki. Zanurz się w świat streamingu, poznaj platformy takie jak Twitch czy YouTube i zacznij nadawać. Skuteczne praktyki streamingu pomogą Ci nawiązać silną więź z widzami i zwiększyć widoczność Twoich treści online.

Pomyśl o strumieniu jako o strumieniu danych, a o łańcuchu wywołań metod jako o potoku. Takie podejście umożliwia wydajne przetwarzanie i transformację danych, zapewniając wysoką wydajność i elastyczność w rozwoju oprogramowania. Strumienie zapewniają ciągłe przetwarzanie danych, podczas gdy potoki pomagają organizować sekwencję operacji, czyniąc aplikację bardziej ustrukturyzowaną i łatwiejszą w utrzymaniu.

Każda metoda pośrednia otrzymuje wynik poprzedniego etapu, reprezentowany jako strumień. Wykonuje swoje zadanie i zwraca nowy strumień, który jest przekazywany do następnej metody. Takie podejście zapewnia modułowość i upraszcza przetwarzanie danych, umożliwiając efektywne zarządzanie przepływem informacji.

Ostatnia lub końcowa metoda w programowaniu może nie zwracać żadnej wartości (void) lub zwracać wynik inny niż strumień.

Przygotowywanie danych

Metody Java Stream API zostaną zademonstrowane na przykładzie biblioteki offline. W tej bibliotece dostępne są takie dane, jak autor, tytuł i rok wydania każdej książki. Korzystając z Java Stream API, można efektywnie przetwarzać i analizować dane książki, co upraszcza różne operacje, takie jak filtrowanie, sortowanie i agregowanie informacji. Takie podejście znacznie poprawia wydajność i czytelność kodu, czyniąc go bardziej zrozumiałym i łatwiejszym w utrzymaniu.

W naszej bibliotece będziemy przechowywać informacje o czytelnikach, w tym ich nazwisko, imię, patronim i adres e-mail. Każdy czytelnik ma możliwość wzięcia jednej lub kilku książek z biblioteki, a informacje o nich również będą przechowywane w naszej bazie danych. Pozwoli nam to efektywnie zarządzać wypożyczaniem książek i utrzymywać kontakt z czytelnikami.

Aby skutecznie komunikować się z czytelnikami, konieczne jest uzyskanie ich zgody na powiadomienia e-mail. Biblioteka będzie wysyłać newslettery z przypomnieniami o terminach zwrotu książek, a także aktualnymi wiadomościami i wydarzeniami. Pomoże nam to utrzymać kontakt z użytkownikami i poprawić jakość ich usług.

Przykład kodu Java demonstruje jego składnię i podstawowe zasady programowania. Java to język obiektowy wysokiego poziomu, szeroko stosowany do tworzenia różnorodnych aplikacji, od mobilnych po usługi sieciowe. W tym przykładzie zobaczysz, jak tworzyć klasy, metody i korzystać z podstawowych konstrukcji języka. Java zapewnia niezależność od platformy, co czyni ją idealnym wyborem do tworzenia oprogramowania, które musi działać w różnych systemach operacyjnych.

Oto prosty przykład kodu Java ilustrujący tworzenie klasy i metody wyświetlającej komunikat na ekranie.

«`java
public class HelloWorld {
public static void main(String[] args) {
System.out.println(«Hello, world!»);
}
}
«`

Ten przykład tworzy klasę `HelloWorld` i wyświetla tekst «Hello, world!» w metodzie `main`. Ten kod jest podstawowym przykładem użycia Javy i jest odpowiedni dla początkujących programistów, którzy chcą poznać podstawy języka. Java oferuje potężne narzędzia i biblioteki, które umożliwiają tworzenie skalowalnych i bezpiecznych aplikacji, co czyni ją jednym z najpopularniejszych języków programowania na świecie.

Do sortowania — sorted()

Ta metoda służy do sortowania elementów w strumieniu. Domyślnie elementy są sortowane w kolejności rosnącej. W przypadku wartości liczbowych sortowanie jest intuicyjne; należy jednak pamiętać, że wielkie i małe litery są przetwarzane oddzielnie, co może mieć wpływ na wynik sortowania. Jest to istotne podczas pracy z danymi tekstowymi, aby uzyskać oczekiwaną kolejność elementów.

Należy pamiętać, że ta metoda jest przeznaczona wyłącznie do sortowania obiektów implementujących interfejs Comparable. Korzystanie z tego interfejsu pozwala na ustalenie naturalnej kolejności sortowania obiektów, co jest kluczowym aspektem podczas pracy ze zbiorami danych. Sortowanie za pomocą interfejsu Comparable upraszcza proces porównywania obiektów i poprawia wydajność algorytmów sortowania.

Jeśli klasy obiektów nie implementują tego interfejsu lub wymagana jest niestandardowa logika sortowania, można przekazać własny algorytm porównywania elementów jako argument. Pozwala to na elastyczne dostosowywanie procesu sortowania i dostosowywanie go do konkretnych zadań, zapewniając tym samym maksymalną wydajność i łatwość korzystania z danych.

Użycie tej metody generuje nowy strumień danych.

Uzyskaj pełną listę książek bibliotecznych posortowanych według roku wydania. Umożliwi Ci to łatwe wyszukiwanie literatury według daty publikacji i śledzenie zmian w tematyce i gatunkach książek na przestrzeni lat. Przeglądaj archiwum i wybieraj interesujące Cię dzieła, aby pogłębić swoją wiedzę lub znaleźć inspirację w literaturze.

Interfejs List oferuje metodę sortowania elementów — sort(). Metoda ta pozwala na przekazanie algorytmu porównania, który określa kolejność sortowania. Przed pojawieniem się wyrażeń lambda programiści tworzyli własne klasy implementujące interfejs Comparator lub korzystali z klas anonimowych do definiowania logiki porównania. Dzięki wyrażeniom lambda proces sortowania stał się prostszy i bardziej przejrzysty, co znacznie uprościło kod i poprawiło jego czytelność.

Metoda sort() nie zwraca wyniku, ale modyfikuje oryginalną kolekcję. W tym przykładzie sortowanie musi zostać przeniesione do osobnej metody, aby zachować aktualną kolejność książek. Pozwala to uniknąć niepożądanych zmian w oryginalnej kolekcji i zapewnia bardziej elastyczne zarządzanie danymi. Sortowanie w osobnej metodzie upraszcza pracę z kolekcją i sprawia, że ​​kod jest bardziej zrozumiały i wygodny do dalszej manipulacji.

Wyrażenie lambda Comparator.comparing(Book::getIssueYear) służy do przekazania algorytmu porównywania elementów do metody sorted(). Takie podejście umożliwia wygodne sortowanie obiektów klasy Book według roku publikacji. Użycie wyrażeń lambda w Javie upraszcza tworzenie komparatorów i zwiększa czytelność kodu. Sortowanie według roku publikacji może być przydatne w różnych aplikacjach, takich jak biblioteki czy księgarnie, gdzie kolejność publikacji ma znaczenie.

Jest to podobne do klasy anonimowej w powyższym przykładzie: oznacza to, że książki są porównywane na podstawie roku publikacji.

Metoda collect(Collectors.toList()) finalizuje strumień i konwertuje jego elementy na listę. Ta metoda jest ważną częścią interfejsu Java Stream API, umożliwiając efektywne zbieranie danych ze strumienia w celu utworzenia łatwej w zarządzaniu struktury. Użycie collect(Collectors.toList()) nie tylko upraszcza przetwarzanie danych, ale także poprawia wydajność aplikacji poprzez optymalizację obsługi kolekcji.

Nie było potrzeby tworzenia osobnej metody sortowania, co uczyniło kod bardziej zwartym i uproszczonym. Takie podejście poprawia czytelność i łatwość utrzymania kodu, co jest ważnym aspektem w tworzeniu oprogramowania.

Do transformacji — map()

Metoda transformacji obiektów jest używana do różnych zadań, w tym do wyodrębniania wartości poszczególnych pól lub tworzenia nowych obiektów na podstawie danych oryginalnego obiektu. Proces ten pozwala na wydajne przetwarzanie i adaptację danych, co zapewnia elastyczność w pracy z różnymi typami informacji. Transformacja obiektów jest ważnym aspektem rozwoju, ponieważ pomaga optymalizować kod i ulepszać strukturę danych.

Musimy utworzyć listę mailingową zawierającą adresy e-mail wszystkich czytelników biblioteki. Nie będziemy brać pod uwagę flagi zgody na otrzymywanie informacji, ponieważ biblioteka jest zamykana i chcemy powiadomić wszystkich o tym zdarzeniu.

W tym przykładzie używamy pętli do iteracji, jednocześnie zmieniając wystąpienie nowej listy. Jeśli zajdzie potrzeba ustawienia warunków filtrowania, projekt stanie się jeszcze bardziej złożony.

Gdy po raz pierwszy używamy funkcji map(), wyodrębniamy adresy e-mail z listy czytelników. Następnie, w kolejnym kroku, tworzymy instancje klasy EmailAddress i zbieramy uzyskane adresy na wspólnej liście. Pozwala to na efektywne zarządzanie i przetwarzanie adresów e-mail, co jest szczególnie ważne w systemach wysyłających listy mailingowe i pracujących z bazami danych klientów.

Do filtrowania — filter()

Metoda filtruje strumień danych zgodnie z określonym predykatem. Pozwala to na sformułowanie warunku w zwartej formie, unikając złożonych konstrukcji if-else.

Aby utworzyć listę mailingową, należy wziąć pod uwagę tylko adresy czytelników, którzy wyrazili zgodę na otrzymywanie informacji. Ważne jest również upewnienie się, że ci czytelnicy wypożyczyli z biblioteki więcej niż jedną książkę. Pozwoli to na stworzenie grupy docelowej zainteresowanej otrzymywaniem newslettera i zwiększy efektywność komunikacji.

W tym kontekście do pętli dodawane są dodatkowe warunki, które będą sprawdzane dla każdego czytelnika. Pozwala to nam precyzyjniej dostosować logikę przetwarzania danych, zapewniając indywidualne podejście do każdego użytkownika.

W pierwszym etapie, używając funkcji filter(), redukujemy liczbę czytelników, koncentrując się na liście użytkowników, którzy wyrazili zgodę na otrzymywanie newslettera. Pozwala nam to zoptymalizować proces interakcji z odbiorcami i zwiększyć efektywność komunikacji.

W drugim etapie wybieramy z ogólnej liczby czytelników osoby, które przeczytały więcej niż jedną książkę.

Korzystając z funkcji map() i collect(), wyodrębniamy adresy e-mail, konwertujemy je do wymaganego formatu i tworzymy z nich listę. Ten proces pozwala nam efektywnie przetwarzać dane i uzyskiwać ustrukturyzowany wynik do dalszego wykorzystania.

Do transformacji i tworzenia listy liniowej — flatMap()

Metoda flatMap() wykonuje swoją pracę w dwóch etapach, co znajduje odzwierciedlenie w jej nazwie. Łączy ona dwie główne operacje: najpierw transformację elementów, a następnie „spłaszczanie” struktury danych. Najpierw każdy element tablicy źródłowej jest transformowany za pomocą podanej funkcji, a następnie wszystkie wynikowe tablice są łączone w jedną tablicę. Ta metoda jest szczególnie przydatna podczas pracy z zagnieżdżonymi strukturami danych, ponieważ pozwala uprościć i uczynić kod bardziej czytelnym. Użycie funkcji flatMap() optymalizuje proces przetwarzania danych, minimalizując liczbę kroków i upraszczając logikę manipulacji tablicami.

  • map (wiemy już, że to transformacja);
  • i flat – dosłownie „flat”.

Używając funkcji map() do przetwarzania strumienia list, wynikiem będzie strumień zawierający listy w listach, czyli List>. To zachowanie wynika z faktu, że map() stosuje daną funkcję do każdego elementu strumienia źródłowego, w tym przypadku do list. Zatem struktura wyjściowa to taka, w której każdy element jest listą, która może wymagać dodatkowego przetwarzania w celu uzyskania listy płaskiej lub innej pożądanej struktury danych.

Metoda flatMap() służy do tworzenia jednowymiarowej listy, do której sekwencyjnie dołączane są przekształcone wartości ze wszystkich list uzyskanych metodą map(). Pozwala to na wydajne przetwarzanie zagnieżdżonych struktur danych i upraszcza pracę z tablicami, zapewniając bardziej zwarty i wygodny wynik. Użycie flatMap() poprawia czytelność kodu i optymalizuje proces transformacji danych, co czyni tę funkcję niezbędnym narzędziem w nowoczesnym programowaniu.

map vs. flatMap

Rozważmy sytuację, w której ta operacja jest szczególnie Przydatne.

Utwórz unikalną listę wszystkich książek wypożyczonych przez czytelników. Na tej liście musimy wyeliminować duplikaty, tak aby każda książka tego samego autora o tym samym tytule i roku wydania pojawiała się tylko raz.

Aby uzyskać listę unikatowych książek, użyliśmy struktury danych Set. Najpierw sekwencyjnie przetworzyliśmy wszystkich czytelników i dodaliśmy ich książki do tego zestawu. Po zakończeniu tego procesu przekonwertowaliśmy zestaw na listę (ArrayList), co pozwoliło nam uzyskać ostateczną listę unikatowych książek.

Po użyciu metody flatMap() otrzymujemy strumień zawierający wszystkie książki od wszystkich czytelników. Metoda distinct() pozwala nam pozostawić w tym strumieniu tylko unikatowe wartości, co gwarantuje brak duplikatów. Zatem połączenie flatMap() i distinct() efektywnie przetwarza dane, umożliwiając nam pracę z unikatowymi rekordami w zbiorze książek.

Optymalizacja procesów może odbywać się bez konieczności implementacji dodatkowych pól i pętli. Pozwala nam to uprościć strukturę i zwiększyć wydajność pracy. Eliminacja zbędnych elementów sprzyja szybszemu przetwarzaniu danych i poprawia komfort użytkowania. Korzystanie z prostych i zrozumiałych rozwiązań jest kluczem do tworzenia efektywnych systemów.

Aby sprawdzić, czy istnieje coś pasującego, użyj metody anyMatch()

Prosta metoda, która przyjmuje warunek predykatu i zwraca flagę logiczną. Ta metoda pozwala efektywnie sprawdzić, czy określony warunek jest spełniony, co może być przydatne w różnych zadaniach programistycznych. Dzięki temu podejściu programiści mogą uprościć proces podejmowania decyzji w kodzie, zapewniając czystszą i bardziej zrozumiałą strukturę. Metoda opiera się na operacjach logicznych, co czyni ją uniwersalnym narzędziem do rozwiązywania wielu problemów.

  • true, jeśli w strumieniu znajduje się obiekt spełniający warunek;
  • false – jeśli taki obiekt nie istnieje.

Sprawdź, czy któryś z czytelników Twojej biblioteki wypożyczył książki George'a Orwella. Ciekawostką jest, jak popularne są jego dzieła wśród odwiedzających. Orwell jest znaczącą postacią w literaturze, a jego dzieła, takie jak „Rok 1984” i „Folwark zwierzęcy”, wciąż budzą zainteresowanie i dyskusje. Upewnij się, że jego książki są dostępne w Twojej bibliotece, aby spełnić prośby czytelników.

Utwórzmy dwie zagnieżdżone pętle i wprowadźmy dodatkową zmienną do przechowywania wyniku pośredniego. Pętla zostanie przerwana przy pierwszym zetknięciu z dziełem Orwella.

Metoda flatMap() pozwala nam utworzyć strumień łączący wszystkie książki w jedną całość, a metoda anyMatch() sprawdza, czy nazwisko Orwella znajduje się wśród autorów. To efektywny sposób filtrowania danych, pozwalający na szybkie znalezienie potrzebnych elementów w kolekcji.

Aby pozostać tylko jednym - reduce()

Metoda reduce() pozwala przekonwertować tablicę na pojedynczą wartość, redukując ją do pojedynczego wyniku. Aby użyć tej metody, należy przekazać wartość początkową (która jest opcjonalnym parametrem) oraz funkcję akumulatora przyjmującą dwa parametry. Funkcja ta przetwarza każdy element tablicy, sekwencyjnie stosując określoną logikę, dzięki czemu metoda reduce() jest potężnym narzędziem do wykonywania różnych obliczeń i agregacji danych w JavaScript.

Ta funkcja jest najpierw stosowana do wartości początkowej i pierwszego elementu strumienia danych. Wynik tego kroku jest następnie łączony z kolejnym elementem strumienia i tak dalej, aż wszystkie elementy zostaną przetworzone. W ten sposób funkcja sekwencyjnie przetwarza każdy element, zaczynając od wartości początkowej, co pozwala na efektywną pracę ze strumieniami danych.

Istnieją bardziej złożone metody redukcji, które wymagają dodania trzeciego parametru — funkcji łączącej. Ten parametr jest szczególnie przydatny w przypadkach, gdy wymagana jest paralelizacja lub gdy typy argumentów funkcji akumulatora nie pasują do typu wyniku funkcji. Funkcja łączenia umożliwia efektywne przetwarzanie danych i łączenie wyników, co poprawia wydajność i ułatwia pracę z różnymi typami danych.

Obliczanie wartości maksymalnej za pomocą funkcji reduce()

Oczywiście chętnie pomogę w edycji tekstu. Proszę podać kod źródłowy do refaktoryzacji.

Określ maksymalną liczbę książek, jaką posiada obecnie czytelnik.

Na początku procesu ustawiamy minimalną liczbę książek, jaką posiada każdy czytelnik, na zero. Następnie przechodzimy przez wszystkich czytelników, analizując liczbę książek każdego z nich i aktualizując maksymalną liczbę książek, jeśli to konieczne. Pozwala nam to określić, którzy czytelnicy mają najwięcej książek, co może być przydatne do dalszej analizy i rekomendacji.

W pierwszym kroku używamy metody map(), aby zmapować każdego czytelnika na liczbę wypożyczonych przez niego książek. Następnie używamy metody reduce(), aby określić maksymalną wartość w wynikowym strumieniu danych. Ten proces pozwala nam efektywnie analizować wzorce czytania wśród użytkowników i identyfikować tych, którzy wypożyczyli najwięcej książek.

Każde wywołanie funkcji akumulatora w JavaScript tworzy nowy obiekt. Jest to ważne, zwłaszcza jeśli chcesz, aby wynikiem metody reduce() był obiekt złożony, taki jak kolekcja. W takim przypadku nowa kolekcja będzie tworzona na każdym etapie funkcji akumulatora. Może to prowadzić do nieefektywnego wykorzystania pamięci i obniżenia wydajności, jeśli proces nie jest zoptymalizowany. Aby uniknąć niepotrzebnego zużycia zasobów, można zastosować metody umożliwiające modyfikację istniejącego obiektu zamiast tworzenia nowego na każdym kroku.

Negatywny wpływ na wydajność może być znaczący. W takich sytuacjach zaleca się skorzystanie z metody collect(), która pozwala zoptymalizować przetwarzanie danych i zwiększyć wydajność aplikacji.

Zawód programisty Java

Nauczysz się programowania w Javie od podstaw i będziesz tworzyć aplikacje internetowe, korzystając z frameworka Spring. W ciągu sześciu miesięcy zdobędziesz podstawowe umiejętności i zbudujesz portfolio, a my pomożemy Ci znaleźć pracę.

Dowiedz się więcej