Suorituskyky Stream API:ssa
Olet onnistuneesti refaktoroinut koodin, tehden siitä selkeämmän, lyhyemmän ja ilmaisullisemman.
Mutta entä suorituskyky? Kuinka tehokas Stream API on verrattuna perinteisiin silmukoihin? Voiko sitä nopeuttaa käyttämällä parallelStream()-menetelmää? Otetaan siitä selvää!
Suoritusajan mittaaminen
Objektiivisen suorituskyvyn vertailun vuoksi luodaan testiaineisto, jossa on 100 000 käyttäjää ja erilaisia tilauksia. Toteutetaan kolme suodatustapaa:
- Perinteinen
for-loop– klassinen tapa sisäkkäisillä silmukoilla; - Stream API (
stream()) – moderni deklaratiivinen menetelmä; - Parallel Stream API (
parallelStream()) – monisäikeinen käsittely.
Suoritusajan mittaamiseen käytetään System.nanoTime(), joka tarjoaa tarkan aikavälin mittauksen.
Testin toteutus
Luodaan 100 000 käyttäjää, joilla kaikilla on yli 10 000 dollarin tilaukset, ja ajetaan kaikki kolme menetelmää nähdäksemme, mikä niistä on paras suorituskyvyltään.
Main.java
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = generateUsers(100000); // Measuring traditional loop execution time long startTime = System.nanoTime(); List<User> premiumUsersLoop = new ArrayList<>(); for (User user : users) { if (user.isActive()) { int totalOrders = 0; for (Order order : user.getOrders()) { if (order.getTotal() >= 10000) { totalOrders++; } } if (totalOrders >= 3) { premiumUsersLoop.add(user); } } } long endTime = System.nanoTime(); System.out.println("For-loop: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersStream = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Parallel Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersParallelStream = users.parallelStream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Parallel Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); } private static List<User> generateUsers(int count) { List<User> users = new ArrayList<>(); for (int i = 0; i < count; i++) { users.add(new User("User" + i, true, List.of( new Order(14000), new Order(5000), new Order(7000) ))); } return users; } } class Order { private final double total; public Order(double total) { this.total = total; } public double getTotal() { return total; } } class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } @Override public String toString() { return "User{name='" + name + "'}"; } }
Suoritettaessa testiä 100 000 käyttäjällä voidaan havaita seuraavia suuntauksia:
Perinteinen for-loop on nopeampi yksinkertaisissa operaatioissa, koska se välttää streamin luonnista ja ylimääräisistä olioista aiheutuvan ylikuorman. Se toimii parhaiten, kun raaka suorituskyky on etusijalla.
Stream API on joskus hieman hitaampi ylimääräisten stream-olioiden vuoksi, mutta se parantaa merkittävästi koodin luettavuutta ja ylläpidettävyyttä.
Rinnakkaisella Stream API:lla voidaan nopeuttaa käsittelyä moniydinkoneilla, mutta ei aina. Jos aineisto on pieni, säikeiden hallinnasta aiheutuva ylikuorma voi itse asiassa hidastaa toimintaa. Se soveltuu parhaiten raskaaseen laskentaan, mutta ei silloin, kun muokataan jaettuja muuttujia, koska säikeet toimivat itsenäisesti.
Yhteenveto
Stream API on tehokas työkalu, joka tekee koodista luettavampaa ja ytimekkäämpää. Kuitenkin suorituskyvyn osalta on tärkeää ymmärtää sen rajoitukset. Joissakin tapauksissa perinteiset for-silmukat ovat nopeampia, erityisesti pienten tietoaineistojen kanssa työskenneltäessä. parallelStream() voi parantaa käsittelynopeutta, mutta sen hyöty tulee varmistaa testaamalla.
Oikean lähestymistavan valinnan tulisi olla tarkoituksellista: jos luettavuus on etusijalla, käytä Stream API:a; jos suorituskyky on kriittistä, testaa ja mittaa!
1. Mikä lähestymistapa on tyypillisesti nopein yksinkertaisissa operaatioissa?
2. Miksi Stream API voi olla hitaampi kuin tavallinen silmukka?
3. Milloin parallelStream() voi hidastaa suoritusta?
Kiitos palautteestasi!
Kysy tekoälyä
Kysy tekoälyä
Kysy mitä tahansa tai kokeile jotakin ehdotetuista kysymyksistä aloittaaksesi keskustelumme
Can you explain when it's best to use Stream API versus traditional loops?
How does parallelStream() actually work under the hood?
What are some real-world scenarios where parallelStream() would be beneficial?
Awesome!
Completion rate improved to 2.33
Suorituskyky Stream API:ssa
Pyyhkäise näyttääksesi valikon
Olet onnistuneesti refaktoroinut koodin, tehden siitä selkeämmän, lyhyemmän ja ilmaisullisemman.
Mutta entä suorituskyky? Kuinka tehokas Stream API on verrattuna perinteisiin silmukoihin? Voiko sitä nopeuttaa käyttämällä parallelStream()-menetelmää? Otetaan siitä selvää!
Suoritusajan mittaaminen
Objektiivisen suorituskyvyn vertailun vuoksi luodaan testiaineisto, jossa on 100 000 käyttäjää ja erilaisia tilauksia. Toteutetaan kolme suodatustapaa:
- Perinteinen
for-loop– klassinen tapa sisäkkäisillä silmukoilla; - Stream API (
stream()) – moderni deklaratiivinen menetelmä; - Parallel Stream API (
parallelStream()) – monisäikeinen käsittely.
Suoritusajan mittaamiseen käytetään System.nanoTime(), joka tarjoaa tarkan aikavälin mittauksen.
Testin toteutus
Luodaan 100 000 käyttäjää, joilla kaikilla on yli 10 000 dollarin tilaukset, ja ajetaan kaikki kolme menetelmää nähdäksemme, mikä niistä on paras suorituskyvyltään.
Main.java
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { List<User> users = generateUsers(100000); // Measuring traditional loop execution time long startTime = System.nanoTime(); List<User> premiumUsersLoop = new ArrayList<>(); for (User user : users) { if (user.isActive()) { int totalOrders = 0; for (Order order : user.getOrders()) { if (order.getTotal() >= 10000) { totalOrders++; } } if (totalOrders >= 3) { premiumUsersLoop.add(user); } } } long endTime = System.nanoTime(); System.out.println("For-loop: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersStream = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); // Measuring Parallel Stream API execution time startTime = System.nanoTime(); List<User> premiumUsersParallelStream = users.parallelStream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .filter(order -> order.getTotal() >= 10000) .count() >= 3) .toList(); endTime = System.nanoTime(); System.out.println("Parallel Stream API: " + (endTime - startTime) / 1_000_000 + " ms"); } private static List<User> generateUsers(int count) { List<User> users = new ArrayList<>(); for (int i = 0; i < count; i++) { users.add(new User("User" + i, true, List.of( new Order(14000), new Order(5000), new Order(7000) ))); } return users; } } class Order { private final double total; public Order(double total) { this.total = total; } public double getTotal() { return total; } } class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } @Override public String toString() { return "User{name='" + name + "'}"; } }
Suoritettaessa testiä 100 000 käyttäjällä voidaan havaita seuraavia suuntauksia:
Perinteinen for-loop on nopeampi yksinkertaisissa operaatioissa, koska se välttää streamin luonnista ja ylimääräisistä olioista aiheutuvan ylikuorman. Se toimii parhaiten, kun raaka suorituskyky on etusijalla.
Stream API on joskus hieman hitaampi ylimääräisten stream-olioiden vuoksi, mutta se parantaa merkittävästi koodin luettavuutta ja ylläpidettävyyttä.
Rinnakkaisella Stream API:lla voidaan nopeuttaa käsittelyä moniydinkoneilla, mutta ei aina. Jos aineisto on pieni, säikeiden hallinnasta aiheutuva ylikuorma voi itse asiassa hidastaa toimintaa. Se soveltuu parhaiten raskaaseen laskentaan, mutta ei silloin, kun muokataan jaettuja muuttujia, koska säikeet toimivat itsenäisesti.
Yhteenveto
Stream API on tehokas työkalu, joka tekee koodista luettavampaa ja ytimekkäämpää. Kuitenkin suorituskyvyn osalta on tärkeää ymmärtää sen rajoitukset. Joissakin tapauksissa perinteiset for-silmukat ovat nopeampia, erityisesti pienten tietoaineistojen kanssa työskenneltäessä. parallelStream() voi parantaa käsittelynopeutta, mutta sen hyöty tulee varmistaa testaamalla.
Oikean lähestymistavan valinnan tulisi olla tarkoituksellista: jos luettavuus on etusijalla, käytä Stream API:a; jos suorituskyky on kriittistä, testaa ja mittaa!
1. Mikä lähestymistapa on tyypillisesti nopein yksinkertaisissa operaatioissa?
2. Miksi Stream API voi olla hitaampi kuin tavallinen silmukka?
3. Milloin parallelStream() voi hidastaa suoritusta?
Kiitos palautteestasi!