Manchmal möchten Sie a Stream
mit mehr als einer Bedingung filtern :
myList.stream().filter(x -> x.size() > 10).filter(x -> x.isCool()) ...
oder Sie könnten dasselbe mit einer komplexen Bedingung und einer einzigen tun filter
:
myList.stream().filter(x -> x.size() > 10 && x -> x.isCool()) ...
Ich vermute, dass der zweite Ansatz bessere Leistungseigenschaften aufweist, aber ich weiß es nicht.
Der erste Ansatz gewinnt an Lesbarkeit, aber was ist besser für die Leistung?
Antworten:
Der Code, der für beide Alternativen ausgeführt werden muss, ist so ähnlich, dass Sie ein Ergebnis nicht zuverlässig vorhersagen können. Die zugrunde liegende Objektstruktur kann unterschiedlich sein, aber das ist keine Herausforderung für den Hotspot-Optimierer. Es hängt also von anderen Umgebungsbedingungen ab, die zu einer schnelleren Ausführung führen, wenn es einen Unterschied gibt.
Durch das Kombinieren von zwei Filterinstanzen werden mehr Objekte und damit mehr delegierender Code erstellt. Dies kann sich jedoch ändern, wenn Sie Methodenreferenzen anstelle von Lambda-Ausdrücken verwenden, z . B. Ersetzen
filter(x -> x.isCool())
durchfilter(ItemType::isCool)
. Auf diese Weise haben Sie die synthetische Delegierungsmethode entfernt, die für Ihren Lambda-Ausdruck erstellt wurde. Wenn Sie also zwei Filter mit zwei Methodenreferenzen kombinieren, wird möglicherweise derselbe oder ein geringerer Delegierungscode als mit einem einzelnenfilter
Aufruf mit einem Lambda-Ausdruck mit erstellt&&
.Wie bereits erwähnt, wird diese Art von Overhead durch den HotSpot-Optimierer beseitigt und ist vernachlässigbar.
Theoretisch könnten zwei Filter einfacher parallelisiert werden als ein einzelner Filter, aber das ist nur für ziemlich rechenintensive Aufgaben relevant¹.
Es gibt also keine einfache Antwort.
Unter dem Strich sollten Sie nicht an solche Leistungsunterschiede unterhalb der Geruchserkennungsschwelle denken. Verwenden Sie, was besser lesbar ist.
¹… und würde eine Implementierung erfordern, die die parallele Verarbeitung nachfolgender Stufen durchführt, ein Weg, den die Standard-Stream-Implementierung derzeit nicht einschlägt
quelle
Eine komplexe Filterbedingung ist in Bezug auf die Leistung besser, aber die beste Leistung zeigt, dass eine Schleife mit einem Standard
if clause
die beste Option ist. Der Unterschied auf einem kleinen Array 10 Elemente Unterschied kann ~ 2 mal sein, für ein großes Array ist der Unterschied nicht so groß.Sie können sich mein GitHub-Projekt ansehen , in dem ich Leistungstests für mehrere Array-Iterationsoptionen durchgeführt habe
Für kleine Arrays 10 Elemente Durchsatz ops / s: Für mittlere 10.000 Elemente Durchsatz ops / s: Für große Arrays 1.000.000 Elemente Durchsatz ops / s:
HINWEIS: Tests werden fortgesetzt
UPDATE: Java 11 hat einige Fortschritte bei der Leistung, aber die Dynamik bleibt gleich
Benchmark-Modus: Durchsatz, Betrieb / Zeit
quelle
Dieser Test zeigt, dass Ihre zweite Option eine deutlich bessere Leistung erbringen kann. Ergebnisse zuerst, dann der Code:
jetzt der Code:
quelle
Test #1: {count=100, sum=7207, min=65, average=72.070000, max=91} Test #3: {count=100, sum=7959, min=72, average=79.590000, max=97} Test #2: {count=100, sum=8869, min=79, average=88.690000, max=110}
Dies ist das Ergebnis der 6 verschiedenen Kombinationen des von @Hank D gemeinsam genutzten Beispieltests. Es ist offensichtlich, dass das Prädikat der Form
u -> exp1 && exp2
in allen Fällen sehr leistungsfähig ist.quelle