Periaatteet
Stream API:n toimintaperiaatteet perustuvat funktionaalisen ohjelmoinnin ja laiskan arvioinnin keskeisiin käsitteisiin. Tarkastellaan Stream API:n tärkeimpiä periaatteita.
Laiska arviointi
Yksi Stream API:n keskeisistä periaatteista on laiska arviointi.
Tämä tarkoittaa, että välioperaatioita kuten filter() tai map() ei suoriteta välittömästi. Ne muodostavat vain toimintojen ketjun, joka suoritetaan vasta, kun kutsutaan pääteoperaatio, kuten collect() tai forEach().
Main.java
12345678910111213package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); long count = names.stream() .filter(name -> name.startsWith("A")) // Forming the operation .count(); // Triggering execution System.out.println(count); // Output: 1 } }
Tämä koodi luo virran merkkijonolistasta, suodattaa alkiot säilyttäen vain ne, jotka alkavat kirjaimella A, ja laskee, kuinka moni täyttää ehdon. Tulos (1) tulostetaan, koska vain Alice täyttää ehdon.
Funktionaalinen ohjelmointityyli
Stream API hyödyntää lambda-lausekkeita ja funktionaalisia rajapintoja datan käsittelyyn. Imperatiivisen silmukoihin perustuvan lähestymistavan sijaan kuvataan, mitä datalla halutaan tehdä:
Main.java
1234567891011121314package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> fruits = List.of("apple", "banana", "cherry", "apricot", "blueberry"); List<String> result = fruits.stream() .map(String::toUpperCase) // Convert all strings to uppercase .skip(3) .toList(); // Collect the result into a new list System.out.println(result); } }
Tämä koodi luo virran hedelmälistasta, muuntaa merkkijonot isoiksi kirjaimiksi käyttäen map():ia ja ohittaa kolme ensimmäistä alkiota skip():n avulla. Tulos kerätään uuteen listaan ja tulostetaan alkaen neljännestä alkiosta.
Datan muuttumattomuus
Stream API ei muuta alkuperäistä dataa. Kaikki operaatiot luovat uuden virran tai palauttavat tuloksen muuttamatta kokoelmaa tai taulukkoa. Tämä parantaa datan turvallisuutta ja estää odottamattomat sivuvaikutukset.
Main.java
12345678910111213package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); List<String> updateNames = names.stream() .filter(name -> name.startsWith("A")) .toList(); System.out.println(names); // Output: [Alice, Bob, Charlie] } }
Stream API suodattaa listan nimet luodakseen uuden virran, updateNames, joka sisältää vain ne alkiot, jotka alkavat kirjaimella A. Alkuperäinen listan nimet kuitenkin säilyy muuttumattomana, sillä Stream API ei muokkaa dataa suoraan, mikä varmistaa datan turvallisuuden ja estää sivuvaikutukset.
Virrat ovat kertakäyttöisiä
Virtaa voidaan käyttää vain yhden kerran. Kun pääteoperaatio on suoritettu, virta ei ole enää käytettävissä jatkokäsittelyyn.
Main.java
123456789101112package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream<String> stream = List.of("Alice", "Bob").stream(); stream.forEach(System.out::println); stream.forEach(System.out::println); // Error: Stream has already been used } }
Tässä luodaan virta merkkijonolistasta ja tulostetaan jokainen alkio konsoliin. Kun virtaa on käytetty ensimmäisen kerran, sitä ei voi käyttää uudelleen, mikä aiheuttaa virheen, jos yrität kutsua forEach() uudelleen.
Rinnakkaiskäsittely
Stream API tukee rinnakkaisvirtoja, mikä mahdollistaa nopeamman datan käsittelyn moniydinkoneilla. Tätä käsitellään tarkemmin täällä
Main.java
1234567891011package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5); numbers.parallelStream() .forEach(System.out::println); // Elements are processed in parallel } }
Tämä koodi luo rinnakkaisvirran lukulistasta ja tulostaa jokaisen alkion konsoliin. Käyttämällä parallelStream() mahdollistetaan listan alkioiden rinnakkaiskäsittely, mikä voi nopeuttaa suoritusta suurten datan määrien kanssa.
Koodin selkeys ja luettavuus
Stream API:n käyttö tekee koodista deklaratiivisempaa. Sen sijaan, että kuvailisit miten tehtävä suoritetaan (esimerkiksi käyttämällä silmukoita), määrittelet mitä tarkalleen täytyy tehdä. Tämä parantaa luettavuutta ja yksinkertaistaa koodin ylläpitoa.
Esimerkki silmukalla:
List<String> names = List.of("Alice", "Bob", "Charlie");
for (String name : names) {
System.out.println(name);
}
Esimerkki Stream API:sta:
List<String> names = List.of("Alice", "Bob", "Charlie");
names.stream().forEach(System.out::println);
Ensimmäisessä silmukkaesimerkissä kuvataan tarkasti, miten listaa käydään läpi ja tulostetaan elementit, kun taas toisessa esimerkissä, jossa käytetään Stream API:a, määritellään vain mitä tulee tehdä: käydä läpi elementit ja tulostaa ne. Tämä tekee koodista deklaratiivisempaa ja luettavampaa.
Kiitos palautteestasi!
Kysy tekoälyä
Kysy tekoälyä
Kysy mitä tahansa tai kokeile jotakin ehdotetuista kysymyksistä aloittaaksesi keskustelumme
Awesome!
Completion rate improved to 2.33
Periaatteet
Pyyhkäise näyttääksesi valikon
Stream API:n toimintaperiaatteet perustuvat funktionaalisen ohjelmoinnin ja laiskan arvioinnin keskeisiin käsitteisiin. Tarkastellaan Stream API:n tärkeimpiä periaatteita.
Laiska arviointi
Yksi Stream API:n keskeisistä periaatteista on laiska arviointi.
Tämä tarkoittaa, että välioperaatioita kuten filter() tai map() ei suoriteta välittömästi. Ne muodostavat vain toimintojen ketjun, joka suoritetaan vasta, kun kutsutaan pääteoperaatio, kuten collect() tai forEach().
Main.java
12345678910111213package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); long count = names.stream() .filter(name -> name.startsWith("A")) // Forming the operation .count(); // Triggering execution System.out.println(count); // Output: 1 } }
Tämä koodi luo virran merkkijonolistasta, suodattaa alkiot säilyttäen vain ne, jotka alkavat kirjaimella A, ja laskee, kuinka moni täyttää ehdon. Tulos (1) tulostetaan, koska vain Alice täyttää ehdon.
Funktionaalinen ohjelmointityyli
Stream API hyödyntää lambda-lausekkeita ja funktionaalisia rajapintoja datan käsittelyyn. Imperatiivisen silmukoihin perustuvan lähestymistavan sijaan kuvataan, mitä datalla halutaan tehdä:
Main.java
1234567891011121314package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> fruits = List.of("apple", "banana", "cherry", "apricot", "blueberry"); List<String> result = fruits.stream() .map(String::toUpperCase) // Convert all strings to uppercase .skip(3) .toList(); // Collect the result into a new list System.out.println(result); } }
Tämä koodi luo virran hedelmälistasta, muuntaa merkkijonot isoiksi kirjaimiksi käyttäen map():ia ja ohittaa kolme ensimmäistä alkiota skip():n avulla. Tulos kerätään uuteen listaan ja tulostetaan alkaen neljännestä alkiosta.
Datan muuttumattomuus
Stream API ei muuta alkuperäistä dataa. Kaikki operaatiot luovat uuden virran tai palauttavat tuloksen muuttamatta kokoelmaa tai taulukkoa. Tämä parantaa datan turvallisuutta ja estää odottamattomat sivuvaikutukset.
Main.java
12345678910111213package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<String> names = List.of("Alice", "Bob", "Charlie"); List<String> updateNames = names.stream() .filter(name -> name.startsWith("A")) .toList(); System.out.println(names); // Output: [Alice, Bob, Charlie] } }
Stream API suodattaa listan nimet luodakseen uuden virran, updateNames, joka sisältää vain ne alkiot, jotka alkavat kirjaimella A. Alkuperäinen listan nimet kuitenkin säilyy muuttumattomana, sillä Stream API ei muokkaa dataa suoraan, mikä varmistaa datan turvallisuuden ja estää sivuvaikutukset.
Virrat ovat kertakäyttöisiä
Virtaa voidaan käyttää vain yhden kerran. Kun pääteoperaatio on suoritettu, virta ei ole enää käytettävissä jatkokäsittelyyn.
Main.java
123456789101112package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { Stream<String> stream = List.of("Alice", "Bob").stream(); stream.forEach(System.out::println); stream.forEach(System.out::println); // Error: Stream has already been used } }
Tässä luodaan virta merkkijonolistasta ja tulostetaan jokainen alkio konsoliin. Kun virtaa on käytetty ensimmäisen kerran, sitä ei voi käyttää uudelleen, mikä aiheuttaa virheen, jos yrität kutsua forEach() uudelleen.
Rinnakkaiskäsittely
Stream API tukee rinnakkaisvirtoja, mikä mahdollistaa nopeamman datan käsittelyn moniydinkoneilla. Tätä käsitellään tarkemmin täällä
Main.java
1234567891011package com.example; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5); numbers.parallelStream() .forEach(System.out::println); // Elements are processed in parallel } }
Tämä koodi luo rinnakkaisvirran lukulistasta ja tulostaa jokaisen alkion konsoliin. Käyttämällä parallelStream() mahdollistetaan listan alkioiden rinnakkaiskäsittely, mikä voi nopeuttaa suoritusta suurten datan määrien kanssa.
Koodin selkeys ja luettavuus
Stream API:n käyttö tekee koodista deklaratiivisempaa. Sen sijaan, että kuvailisit miten tehtävä suoritetaan (esimerkiksi käyttämällä silmukoita), määrittelet mitä tarkalleen täytyy tehdä. Tämä parantaa luettavuutta ja yksinkertaistaa koodin ylläpitoa.
Esimerkki silmukalla:
List<String> names = List.of("Alice", "Bob", "Charlie");
for (String name : names) {
System.out.println(name);
}
Esimerkki Stream API:sta:
List<String> names = List.of("Alice", "Bob", "Charlie");
names.stream().forEach(System.out::println);
Ensimmäisessä silmukkaesimerkissä kuvataan tarkasti, miten listaa käydään läpi ja tulostetaan elementit, kun taas toisessa esimerkissä, jossa käytetään Stream API:a, määritellään vain mitä tulee tehdä: käydä läpi elementit ja tulostaa ne. Tämä tekee koodista deklaratiivisempaa ja luettavampaa.
Kiitos palautteestasi!