Како научити Јава Стреам АПИ [+5 Resources]

U Javi, stream predstavlja sekvencu elemenata nad kojima se mogu primenjivati operacije, bilo uzastopno ili paralelno.

Može biti prisutno proizvoljan broj međufaza (međuoperacija), nakon čega sledi finalna operacija, koja vraća rezultat celog procesa.

Šta je zapravo Stream?

Streamovi se kontrolišu putem Stream API-ja, koji je integrisan u Javu 8.

Zamislite stream kao proizvodnu liniju: sirovine se obrađuju, sortiraju, a zatim pripremaju za distribuciju. U Javi, te sirovine su objekti ili zbirke objekata, operacije su obrada, sortiranje i pakovanje, dok je sama proizvodna linija stream.

Stream se sastoji od sledećih ključnih delova:

  • Početni ulaz
  • Međufaze obrade (međuoperacije)
  • Terminalna operacija
  • Konačni rezultat

Hajde da istražimo neke od bitnih karakteristika streamova u Javi:

  • Stream nije struktura podataka u memoriji. Umesto toga, on je serija nizova, objekata ili kolekcija kojima se upravlja korišćenjem specifičnih metoda.
  • Streamovi su deklarativni, što znači da definišete šta želite da uradite, a ne kako.
  • Mogu se koristiti samo jednom, pošto se podaci ne skladište.
  • Stream ne menja originalnu strukturu podataka. Umesto toga, proizvodi novu strukturu podataka na osnovu nje.
  • Konačni rezultat se dobija kao rezultat izvršavanja poslednje operacije u streamu.

Stream API nasuprot obradi kolekcija

Kolekcija je memorijska struktura podataka koja skladišti i obrađuje podatke. Kolekcije nude strukture podataka kao što su Set, Map, List, itd., za organizovanje podataka. Nasuprot tome, stream predstavlja efikasan način za prenos podataka nakon što prođu kroz niz obrada.

Primer korišćenja ArrayList kolekcije:

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(0, 3);
        System.out.println(list);
    }
}

Output: 
[3]

Kao što vidite u gornjem primeru, moguće je kreirati ArrayList kolekciju, skladištiti podatke u nju i zatim obrađivati te podatke koristeći različite metode.

Korišćenjem stream-a, možete obrađivati postojeće strukture podataka i vraćati nove, modifikovane vrednosti. Ispod je prikazan primer kreiranja ArrayList kolekcije i filtriranja njenih vrednosti pomoću stream-a.

import java.util.ArrayList;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList();

        for (int i = 0; i < 20; i++) {
            list.add(i+1);
        }

        System.out.println(list);

        Stream<Integer> filtered = list.stream().filter(num -> num > 10);
        filtered.forEach(num -> System.out.println(num + " "));
    }
}

#Output

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 

U gornjem primeru, stream je kreiran na osnovu postojeće liste. Lista se iterira kako bi se filtrirale vrednosti veće od 10. Važno je zapamtiti da stream ne skladišti podatke, već samo iterira kroz listu i štampa rezultate. Pokušaj štampanja samog stream-a će umesto toga dati referencu na stream.

Rad sa Java Stream API-jem

Java Stream API prihvata kolekciju elemenata ili niz elemenata kao početni izvor, a zatim primenjuje operacije na tim elementima da bi dobio krajnji rezultat. Stream je u suštini kanal kroz koji prolazi serija elemenata, podvrgavajući se transformacijama.

Stream se može formirati iz raznih izvora, uključujući:

  • Kolekcije poput List ili Set.
  • Nizovi.
  • Fajlovi i putanje fajlova, koristeći bafer.

Postoje dve vrste operacija koje se mogu vršiti na streamu:

  • Međuoperacije
  • Terminalne operacije

Međuoperacije naspram terminalnih operacija

Svaka međuoperacija vraća novi stream koji interno transformiše ulaz koristeći zadatu metodu. U ovoj fazi se ne obrađuje stvarni prolazak kroz elemente; umesto toga, operacije se prenose na sledeći stream. Tek terminalna operacija pokreće prolazak kroz stream, generišući željeni rezultat.

Na primer, ako imate listu od 10 brojeva koju želite da filtrirate i mapirate, nećete odmah proći kroz sve elemente liste kako biste dobili filtriran rezultat i mapirali ga. Umesto toga, pojedinačni elementi se proveravaju, i ako ispunjavaju uslov, biće mapirani. Kreiraju se novi streamovi za svaki element.

Operacija mapiranja će se izvršiti samo nad elementima koji zadovoljavaju filter, a ne nad celom listom. Tek u terminalnoj operaciji se prolazi kroz stream, a rezultati se kombinuju u jedan konačni rezultat.

Nakon izvršavanja terminalne operacije, stream se troši i ne može se više koristiti. Da biste ponovo obavljali iste operacije, potrebno je da kreirate novi stream.

Izvor: The Bored Dev

Nakon površnog razumevanja kako streamovi funkcionišu, detaljnije ćemo se upoznati sa primenom streamova u Javi.

#1. Prazan Stream

Prazan stream se formira korišćenjem metode `empty()` iz Stream API-ja.

import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        Stream emptyStream = Stream.empty();
        System.out.println(emptyStream.count());
    }
}

Output:
0

Ako prikažete broj elemenata u ovom streamu, dobićete izlaz 0, jer je stream prazan i ne sadrži elemente. Prazni streamovi su izuzetno korisni za izbegavanje izuzetaka `NullPointerException`.

#2. Stream iz kolekcija

Kolekcije kao što su List i Set imaju metodu `stream()` pomoću koje možete kreirati stream iz kolekcije. Kreirani stream se zatim može obraditi kako bi se dobio krajnji rezultat.

ArrayList<Integer> list = new ArrayList();

for (int i = 0; i < 20; i++) {
    list.add(i+1);
}

System.out.println(list);

Stream<Integer> filtered = list.stream().filter(num -> num > 10);
filtered.forEach(num -> System.out.println(num + " "));

#Output

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 

#3. Stream iz nizova

Metoda `Arrays.stream()` služi za formiranje stream-a iz niza.

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        String[] stringArray = new String[]{"this", "is", "techblog.co.rs"};
        Arrays.stream(stringArray).forEach(item -> System.out.print(item + " "));
    }
}

#Output

this is techblog.co.rs 

Takođe možete definisati početni i krajnji indeks elemenata pri formiranju niza. Početni indeks je inkluzivan, dok je krajnji indeks ekskluzivan.

String[] stringArray = new String[]{"this", "is", "techblog.co.rs"};
Arrays.stream(stringArray, 1, 3).forEach(item -> System.out.print(item + " "));

Output:
is techblog.co.rs

#4. Pronalaženje minimalnih i maksimalnih brojeva pomoću streamova

Pristup najvećem i najmanjem broju u kolekciji ili nizu može se ostvariti korišćenjem komparatora u Javi. Metode `min()` i `max()` prihvataju komparator i vraćaju `Optional` objekat.

Optional objekat je kontejner koji može sadržati ili ne sadržati ne-null vrednost. Ako sadrži ne-null vrednost, pozivanje metode `get()` na njemu će vratiti tu vrednost.

import java.util.Arrays;
import java.util.Optional;

public class MinMax {
    public static void main(String[] args) {
        Integer[] numbers = new Integer[]{21, 82, 41, 9, 62, 3, 11};

        Optional<Integer> maxValue = Arrays.stream(numbers).max(Integer::compare);
        System.out.println(maxValue.get());

        Optional<Integer> minValue = Arrays.stream(numbers).min(Integer::compare);
        System.out.println(minValue.get());
    }
}

#Output
82
3

Resursi za učenje

Sada kada imate osnovno razumevanje stream-ova u Javi, ovo je 5 resursa koji vam mogu pomoći da bolje savladate Javu 8:

#1. Java 8 u akciji

Ova knjiga služi kao vodič koji pokazuje nove funkcije Jave 8, uključujući streamove, lambda izraze i funkcionalni stil programiranja. Knjiga sadrži i kvizove i pitanja za proveru znanja, kako biste mogli da utvrdite stečeno znanje.

Knjigu možete pronaći u mekom povezu, kao i u formatu audio knjige na Amazonu.

#2. Java 8 Lambdas: Funkcionalno programiranje za mase

Ova knjiga je dizajnirana da nauči iskusne Java SE programere kako dodavanje lambda izraza utiče na Java jezik. Knjiga obuhvata jasna objašnjenja, vežbe sa kodom i primere za savladavanje Java 8 lambda izraza.

Dostupna je u mekom povezu i u Kindle formatu na Amazonu.

#3. Java SE 8 za one nestrpljive

Ako ste iskusan Java SE programer, ova knjiga će vas provesti kroz poboljšanja koja su uvedena u Javi SE 8, API za stream, dodavanje lambda izraza, poboljšanja za konkurentno programiranje u Javi, i neke funkcije iz Jave 7 za koje većina ljudi ne zna.

Knjiga je dostupna samo u mekom povezu na Amazonu.

#4. Naučite Java funkcionalno programiranje sa Lambdas & Streams

Ovaj Udemy kurs razmatra osnove funkcionalnog programiranja u Javi 8 i 9. Fokusira se na lambda izraze, reference metoda, streamove i funkcionalne interfejse.

Uključuje i veliki broj Java zadataka i vežbi koje su relevantne za funkcionalno programiranje.

#5. Java Class Library

Java Class Library je deo osnovne Java specijalizacije koju nudi Coursera. Kurs će vas naučiti kako da pišete type-safe kod koristeći Java Generics, razumećete biblioteku klasa koja sadrži preko 4000 klasa, naučićete kako da radite sa fajlovima i kako da rukujete greškama u toku izvršavanja. Ipak, postoje neki preduslovi za pohađanje ovog kursa:

  • Uvod u Javu
  • Uvod u objektno-orijentisano programiranje sa Javom
  • Objektno-orijentisane hijerarhije u Javi

Završne reči

Java Stream API i uvođenje Lambda funkcija u Javu 8 su pojednostavili i poboljšali mnoge aspekte programiranja u Javi, kao što su paralelna iteracija, funkcionalni interfejsi, smanjenje količine koda, itd.

Međutim, streamovi imaju i neka ograničenja, a najveće je da se mogu koristiti samo jednom. Ako ste Java programer, gore pomenuti resursi vam mogu pomoći da razumete ove teme detaljnije, zato ih obavezno pogledajte.

Možda će vas zanimati i obrada izuzetaka u Javi.