Leistung in der Stream-API
Swipe um das Menü anzuzeigen
Sie haben den Code erfolgreich umgestaltet und ihn übersichtlicher, kürzer und ausdrucksstärker gemacht.
Aber wie sieht es mit der Leistung aus? Wie effizient ist die Stream-API im Vergleich zu herkömmlichen Schleifen? Lässt sich die Ausführung mit parallelStream() beschleunigen? Finden wir es heraus!
Ausführungsgeschwindigkeit messen
Um die Leistung objektiv zu vergleichen, erstellen wir einen Testdatensatz mit 100.000 Benutzern und unterschiedlichen Bestellungen. Anschließend implementieren wir drei Filtermethoden:
- Traditionelle
for-loop– klassischer Ansatz mit verschachtelten Schleifen; - Stream-API (
stream()) – moderne deklarative Methode; - Parallele Stream-API (
parallelStream()) – mehrfädige Verarbeitung.
Die Ausführungszeit wird mit System.nanoTime() gemessen, das hochpräzise Zeitdifferenzen liefert.
Testimplementierung
Sie generieren 100.000 Benutzer, wobei sichergestellt wird, dass alle Bestellungen über $10.000 liegen, und führen alle drei Methoden aus, um die beste Leistung zu ermitteln.
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 + "'}"; } }
Nach dem Ausführen des Tests mit 100.000 Benutzern lassen sich folgende Tendenzen beobachten:
Die traditionelle for-loop ist bei einfachen Operationen schneller, da sie keinen Overhead durch die Erstellung von Streams und zusätzlichen Objekten verursacht. Sie erzielt die besten Ergebnisse, wenn maximale Geschwindigkeit im Vordergrund steht.
Die Stream-API ist aufgrund zusätzlicher Stream-Objekte manchmal etwas langsamer, verbessert jedoch die Lesbarkeit und Wartbarkeit des Codes erheblich.
Die Parallel Stream-API kann die Verarbeitung auf Mehrkernsystemen beschleunigen, aber nicht immer. Ist der Datensatz klein, kann der Overhead für das Thread-Management die Ausführung sogar verlangsamen. Sie eignet sich am besten für rechenintensive Aufgaben, jedoch nicht, wenn gemeinsam genutzte Variablen verändert werden, da die Threads unabhängig voneinander laufen.
Zusammenfassung
Die Stream-API ist ein leistungsstarkes Werkzeug, das Code lesbarer und kürzer macht. Hinsichtlich der Performance ist es jedoch wichtig, ihre Einschränkungen zu verstehen. In manchen Fällen sind traditionelle for-Schleifen schneller, insbesondere bei kleinen Datensätzen. parallelStream() kann die Verarbeitungsgeschwindigkeit erhöhen, erfordert jedoch Tests, um sicherzustellen, dass tatsächlich ein Vorteil entsteht.
Die Wahl des richtigen Ansatzes sollte daher bewusst erfolgen: Wenn Lesbarkeit im Vordergrund steht, die Stream-API verwenden; wenn Performance entscheidend ist, testen und messen!
1. Welcher Ansatz ist für einfache Operationen typischerweise am schnellsten?
2. Warum kann die Stream-API langsamer sein als eine normale Schleife?
3. Wann kann parallelStream() die Ausführung verlangsamen?
Danke für Ihr Feedback!
Fragen Sie AI
Fragen Sie AI
Fragen Sie alles oder probieren Sie eine der vorgeschlagenen Fragen, um unser Gespräch zu beginnen