Alkioiden Käsittely Foreach()-Menetelmällä
Olet jo entuudestaan perehtynyt forEach()-metodiin, sillä olet käyttänyt sitä käytännössä tulostaaksesi jokaisen kokoelman alkion konsoliin. Tarkastellaan sitä nyt tarkemmin ja tutustutaan sen toteutuksiin.
Se tarjoaa kätevän tavan käsitellä dataa funktionaalisella tyylillä. On kuitenkin tärkeää huomioida, että käsittelyjärjestys riippuu virran tyypistä ja käytetystä metodista.
Stream API:ssa on kaksi forEach()-metodin toteutusta. Käydään ne läpi yksitellen.
forEach-metodi
Suorittaa toiminnon jokaiselle alkiolle, mutta käsittelyjärjestystä ei taata rinnakkaisissa virroissa.
void forEach(Consumer<? super T> action)
Metodi forEach() ottaa argumenttina Consumer<T>-rajapinnan—funktionaalisen rajapinnan, joka määrittelee toiminnon jokaiselle virran alkiolle. Sitä käytetään yleisesti lokitukseen, tulostukseen tai sivuvaikutusten suorittamiseen.
Käytännön esimerkki
Verkkokaupassa käyttäjien tulee saada ilmoituksia henkilökohtaisista alennuksista. Koska järjestyksellä ei ole merkitystä, käytetään forEach()-metodia rinnakkaisessa virrassa nopeamman suorituksen saavuttamiseksi.
Main.java
1234567891011121314151617181920212223package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> customers = List.of( "alice@example.com", "bob@example.com", "charlie@example.com" ); // Parallel stream for faster processing Stream<String> customerStream = customers.parallelStream(); customerStream.forEach(email -> sendDiscountEmail(email)); } private static void sendDiscountEmail(String email) { System.out.println("Sending discount email to: " + email); // Simulating email sending process } }
Tämä koodi simuloi henkilökohtaisten alennussähköpostien lähettämistä asiakkaille. Koska käytössä on parallelStream(), sähköpostit lähetetään satunnaisessa järjestyksessä nopeuden parantamiseksi. forEach()-metodi soveltaa annettua toimintoa jokaiseen sähköpostiin.
forEachOrdered-metodi
Suorittaa toiminnon säilyttäen alkioiden järjestyksen, myös rinnakkaisissa streameissa.
void forEachOrdered(Consumer<? super T> action)
Kuten forEach, tämä metodi hyväksyy myös Consumer<T>-rajapinnan, mutta se varmistaa, että alkiot käsitellään samassa järjestyksessä kuin ne esiintyvät alkuperäisessä streamissa. Tämä on hyödyllistä, kun järjestyksen säilyttäminen on olennaista.
Käytännön esimerkki
Kuvittele maksujärjestelmä, jossa jokainen tapahtuma täytyy käsitellä täsmälleen siinä järjestyksessä kuin se saapuu. Jos maksut käsitellään väärässä järjestyksessä, se voi johtaa virheisiin, kuten virheellisiin saldolaskelmiin.
Main.java
123456789101112131415161718192021package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> payments = List.of( "Payment #5001 - $100", "Payment #5002 - $250", "Payment #5003 - $75" ); Stream<String> paymentStream = payments.parallelStream(); paymentStream.forEachOrdered(payment -> processPayment(payment)); } private static void processPayment(String payment) { System.out.println("Processing payment: " + payment); } }
Tämä koodi simuloi maksujen käsittelyjärjestelmää, jossa transaktiot täytyy käsitellä siinä järjestyksessä kuin ne saapuvat. parallelStream()-metodin käyttö parantaa suorituskykyä, mutta forEachOrdered() varmistaa, että järjestys säilyy.
Suorituskykyvertailu: forEach() vs. forEachOrdered()
Tarkastellaan, kuinka paljon nopeammin forEach() suorittaa verrattuna forEachOrdered()-metodiin käytettäessä rinnakkaisvirtoja. Tätä varten luodaan lista, jossa on 10 miljoonaa alkiota, käsitellään ne molemmilla menetelmillä laskemalla jokaisen luvun neliöjuuri ja kirjataan ylös suoritusaika.
Main.java
12345678910111213141516171819202122232425262728293031package com.example; import java.util.List; import java.util.stream.IntStream; import java.util.ArrayList; public class Main { public static void main(String[] args) { List<Integer> numbers = new ArrayList<>(); IntStream.range(0, 10_000_000).forEach(numbers::add); // Create a list with 10 million elements // Measure execution time of `forEach()` long startTime = System.nanoTime(); numbers.parallelStream().forEach(num -> process(num)); long forEachTime = System.nanoTime() - startTime; // Measure execution time of `forEachOrdered()` startTime = System.nanoTime(); numbers.parallelStream().forEachOrdered(num -> process(num)); long forEachOrderedTime = System.nanoTime() - startTime; // Print results System.out.println("forEach execution time: " + forEachTime / 1_000_000 + " ms"); System.out.println("forEachOrdered execution time: " + forEachOrderedTime / 1_000_000 + " ms"); } private static void process(int num) { // Simulate workload Math.sqrt(num); } }
Metodi forEach() käsittelee alkioita ilman järjestyksen säilyttämistä, mahdollistaen virran jakaa tehtävät vapaasti käytettävissä oleville suoritinytimille. Tämä maksimoi suorituskyvyn, sillä jokainen säie voi poimia alkioita missä tahansa järjestyksessä ja käsitellä niitä rinnakkain ilman rajoituksia.
Metodi forEachOrdered() säilyttää alkuperäisen järjestyksen alkioille, mikä vaatii lisättyä synkronointia. Rinnakkaisessa virrassa alkiot jaetaan ensin osioihin käsittelyä varten, mutta niiden tulokset täytyy tämän jälkeen koota oikeaan järjestykseen ennen kuin ne välitetään käsittelymetodille.
1. Minkä funktionaalisen rajapinnan forEach-metodi hyväksyy?
2. Mitä tulostetta tämä koodi voi tuottaa?
Kiitos palautteestasi!
Kysy tekoälyä
Kysy tekoälyä
Kysy mitä tahansa tai kokeile jotakin ehdotetuista kysymyksistä aloittaaksesi keskustelumme
Can you explain the main differences between forEach() and forEachOrdered()?
When should I use forEachOrdered() instead of forEach()?
Can you provide more real-world examples where order matters?
Awesome!
Completion rate improved to 2.33
Alkioiden Käsittely Foreach()-Menetelmällä
Pyyhkäise näyttääksesi valikon
Olet jo entuudestaan perehtynyt forEach()-metodiin, sillä olet käyttänyt sitä käytännössä tulostaaksesi jokaisen kokoelman alkion konsoliin. Tarkastellaan sitä nyt tarkemmin ja tutustutaan sen toteutuksiin.
Se tarjoaa kätevän tavan käsitellä dataa funktionaalisella tyylillä. On kuitenkin tärkeää huomioida, että käsittelyjärjestys riippuu virran tyypistä ja käytetystä metodista.
Stream API:ssa on kaksi forEach()-metodin toteutusta. Käydään ne läpi yksitellen.
forEach-metodi
Suorittaa toiminnon jokaiselle alkiolle, mutta käsittelyjärjestystä ei taata rinnakkaisissa virroissa.
void forEach(Consumer<? super T> action)
Metodi forEach() ottaa argumenttina Consumer<T>-rajapinnan—funktionaalisen rajapinnan, joka määrittelee toiminnon jokaiselle virran alkiolle. Sitä käytetään yleisesti lokitukseen, tulostukseen tai sivuvaikutusten suorittamiseen.
Käytännön esimerkki
Verkkokaupassa käyttäjien tulee saada ilmoituksia henkilökohtaisista alennuksista. Koska järjestyksellä ei ole merkitystä, käytetään forEach()-metodia rinnakkaisessa virrassa nopeamman suorituksen saavuttamiseksi.
Main.java
1234567891011121314151617181920212223package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> customers = List.of( "alice@example.com", "bob@example.com", "charlie@example.com" ); // Parallel stream for faster processing Stream<String> customerStream = customers.parallelStream(); customerStream.forEach(email -> sendDiscountEmail(email)); } private static void sendDiscountEmail(String email) { System.out.println("Sending discount email to: " + email); // Simulating email sending process } }
Tämä koodi simuloi henkilökohtaisten alennussähköpostien lähettämistä asiakkaille. Koska käytössä on parallelStream(), sähköpostit lähetetään satunnaisessa järjestyksessä nopeuden parantamiseksi. forEach()-metodi soveltaa annettua toimintoa jokaiseen sähköpostiin.
forEachOrdered-metodi
Suorittaa toiminnon säilyttäen alkioiden järjestyksen, myös rinnakkaisissa streameissa.
void forEachOrdered(Consumer<? super T> action)
Kuten forEach, tämä metodi hyväksyy myös Consumer<T>-rajapinnan, mutta se varmistaa, että alkiot käsitellään samassa järjestyksessä kuin ne esiintyvät alkuperäisessä streamissa. Tämä on hyödyllistä, kun järjestyksen säilyttäminen on olennaista.
Käytännön esimerkki
Kuvittele maksujärjestelmä, jossa jokainen tapahtuma täytyy käsitellä täsmälleen siinä järjestyksessä kuin se saapuu. Jos maksut käsitellään väärässä järjestyksessä, se voi johtaa virheisiin, kuten virheellisiin saldolaskelmiin.
Main.java
123456789101112131415161718192021package com.example; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> payments = List.of( "Payment #5001 - $100", "Payment #5002 - $250", "Payment #5003 - $75" ); Stream<String> paymentStream = payments.parallelStream(); paymentStream.forEachOrdered(payment -> processPayment(payment)); } private static void processPayment(String payment) { System.out.println("Processing payment: " + payment); } }
Tämä koodi simuloi maksujen käsittelyjärjestelmää, jossa transaktiot täytyy käsitellä siinä järjestyksessä kuin ne saapuvat. parallelStream()-metodin käyttö parantaa suorituskykyä, mutta forEachOrdered() varmistaa, että järjestys säilyy.
Suorituskykyvertailu: forEach() vs. forEachOrdered()
Tarkastellaan, kuinka paljon nopeammin forEach() suorittaa verrattuna forEachOrdered()-metodiin käytettäessä rinnakkaisvirtoja. Tätä varten luodaan lista, jossa on 10 miljoonaa alkiota, käsitellään ne molemmilla menetelmillä laskemalla jokaisen luvun neliöjuuri ja kirjataan ylös suoritusaika.
Main.java
12345678910111213141516171819202122232425262728293031package com.example; import java.util.List; import java.util.stream.IntStream; import java.util.ArrayList; public class Main { public static void main(String[] args) { List<Integer> numbers = new ArrayList<>(); IntStream.range(0, 10_000_000).forEach(numbers::add); // Create a list with 10 million elements // Measure execution time of `forEach()` long startTime = System.nanoTime(); numbers.parallelStream().forEach(num -> process(num)); long forEachTime = System.nanoTime() - startTime; // Measure execution time of `forEachOrdered()` startTime = System.nanoTime(); numbers.parallelStream().forEachOrdered(num -> process(num)); long forEachOrderedTime = System.nanoTime() - startTime; // Print results System.out.println("forEach execution time: " + forEachTime / 1_000_000 + " ms"); System.out.println("forEachOrdered execution time: " + forEachOrderedTime / 1_000_000 + " ms"); } private static void process(int num) { // Simulate workload Math.sqrt(num); } }
Metodi forEach() käsittelee alkioita ilman järjestyksen säilyttämistä, mahdollistaen virran jakaa tehtävät vapaasti käytettävissä oleville suoritinytimille. Tämä maksimoi suorituskyvyn, sillä jokainen säie voi poimia alkioita missä tahansa järjestyksessä ja käsitellä niitä rinnakkain ilman rajoituksia.
Metodi forEachOrdered() säilyttää alkuperäisen järjestyksen alkioille, mikä vaatii lisättyä synkronointia. Rinnakkaisessa virrassa alkiot jaetaan ensin osioihin käsittelyä varten, mutta niiden tulokset täytyy tämän jälkeen koota oikeaan järjestykseen ennen kuin ne välitetään käsittelymetodille.
1. Minkä funktionaalisen rajapinnan forEach-metodi hyväksyy?
2. Mitä tulostetta tämä koodi voi tuottaa?
Kiitos palautteestasi!