Ich bin gerade auf eine Frage gestoßen, als ich a List
und seine stream()
Methode verwendet habe. Obwohl ich weiß, wie man sie benutzt, bin ich mir nicht ganz sicher, wann ich sie benutzen soll.
Zum Beispiel habe ich eine Liste, die verschiedene Pfade zu verschiedenen Orten enthält. Jetzt möchte ich prüfen, ob ein einzelner, angegebener Pfad einen der in der Liste angegebenen Pfade enthält. Ich möchte eine zurückgeben, boolean
basierend darauf, ob die Bedingung erfüllt ist oder nicht.
Dies ist natürlich an sich keine schwere Aufgabe. Aber ich frage mich, ob ich Streams oder eine for (-each) -Schleife verwenden soll.
Die Liste
private static final List<String> EXCLUDE_PATHS = Arrays.asList(new String[]{
"my/path/one",
"my/path/two"
});
Beispiel - Stream
private boolean isExcluded(String path){
return EXCLUDE_PATHS.stream()
.map(String::toLowerCase)
.filter(path::contains)
.collect(Collectors.toList())
.size() > 0;
}
Beispiel - Für jede Schleife
private boolean isExcluded(String path){
for (String excludePath : EXCLUDE_PATHS) {
if(path.contains(excludePath.toLowerCase())){
return true;
}
}
return false;
}
Beachten Sie, dass der path
Parameter immer klein geschrieben ist .
Meine erste Vermutung ist, dass der Ansatz für jeden Ansatz schneller ist, da die Schleife sofort zurückkehren würde, wenn die Bedingung erfüllt ist. Während der Stream immer noch alle Listeneinträge durchläuft, um die Filterung abzuschließen.
Ist meine Annahme richtig? Wenn ja, warum (oder besser wann ) würde ich stream()
dann verwenden?
quelle
new String[]{…}
. Verwenden Sie einfachArrays.asList("my/path/one", "my/path/two")
String[]
ist, müssen Sie nicht anrufenArrays.asList
. Sie können einfach mit über das Array streamenArrays.stream(array)
. Übrigens habe ich Schwierigkeiten, den Zweck desisExcluded
Tests insgesamt zu verstehen . Ist es wirklich interessant, ob ein Element vonEXCLUDE_PATHS
buchstäblich irgendwo im Pfad enthalten ist? DhisExcluded("my/path/one/foo/bar/baz")
wird zurückkehrentrue
, sowieisExcluded("foo/bar/baz/my/path/one/")
...Arrays.stream
Methode nicht bewusst , danke, dass Sie darauf hingewiesen haben. In der Tat scheint das Beispiel, das ich gepostet habe, für andere als mich ziemlich nutzlos zu sein. Ich bin mir des Verhaltens derisExcluded
Methode bewusst , aber es ist wirklich nur etwas, das ich für mich selbst brauche, um Ihre Frage zu beantworten: Ja , es ist aus Gründen interessant, die ich nicht erwähnen möchte, da es nicht in den Anwendungsbereich passt der ursprünglichen Frage.toLowerCase
auf die Konstante angewendet, die bereits in Kleinbuchstaben geschrieben ist? Sollte es nicht auf daspath
Argument angewendet werden ?Antworten:
Ihre Annahme ist richtig. Ihre Stream-Implementierung ist langsamer als die for-Schleife.
Diese Stream-Nutzung sollte jedoch so schnell sein wie die for-Schleife:
Dies durchläuft die Elemente, wendet sie an
String::toLowerCase
und filtert sie einzeln auf die Elemente und endet beim ersten übereinstimmenden Element .Beide
collect()
&anyMatch()
sind Terminaloperationen.anyMatch()
Wird jedoch beim ersten gefundenen Element beendet, währendcollect()
alle Elemente verarbeitet werden müssen.quelle
findFirst()
in Kombination mitfilter()
. Anscheinend nicht weiß so gut, wie ich dachte, wie man Streams benutzt.Die Entscheidung, ob Streams verwendet werden sollen oder nicht, sollte nicht von Leistungsaspekten abhängen, sondern von der Lesbarkeit. Wenn es wirklich um Leistung geht, gibt es andere Überlegungen.
Bei Ihrem
.filter(path::contains).collect(Collectors.toList()).size() > 0
Ansatz verarbeiten Sie alle Elemente und sammeln sie in einem temporären ElementList
, bevor Sie die Größe vergleichen. Dies ist jedoch für einen Stream, der aus zwei Elementen besteht, kaum von Bedeutung.Die Verwendung
.map(String::toLowerCase).anyMatch(path::contains)
kann CPU-Zyklen und Speicher sparen, wenn Sie eine wesentlich größere Anzahl von Elementen haben. Dies konvertiert jedoch jedesString
in seine Kleinbuchstaben-Darstellung, bis eine Übereinstimmung gefunden wird. Offensichtlich hat die Verwendung einen Sinnstattdessen. Sie müssen die Konvertierung in Kleinbuchstaben also nicht bei jedem Aufruf von wiederholen
isExcluded
. Wenn die Anzahl der ElementeEXCLUDE_PATHS
oder die Länge der Zeichenfolgen sehr groß wird, können Sie die Verwendung in Betracht ziehenWenn Sie eine Zeichenfolge als Regex-Muster mit dem
LITERAL
Flag kompilieren , verhält sie sich wie normale Zeichenfolgenoperationen, kann jedoch einige Zeit für die Vorbereitung der Engine aufwenden, z. B. mithilfe des Boyer Moore-Algorithmus, um den tatsächlichen Vergleich effizienter zu gestalten.Dies zahlt sich natürlich nur aus, wenn genügend nachfolgende Tests vorhanden sind, um die für die Vorbereitung aufgewendete Zeit zu kompensieren. Die Feststellung, ob dies der Fall sein wird, ist neben der ersten Frage, ob dieser Vorgang jemals leistungskritisch sein wird, eine der tatsächlichen Leistungsüberlegungen. Nicht die Frage, ob Streams oder
for
Loops verwendet werden sollen.Übrigens behalten die obigen Codebeispiele die Logik Ihres ursprünglichen Codes bei, was für mich fragwürdig erscheint. Ihre
isExcluded
Methode gibt zurücktrue
, wenn der angegebene Pfad eines der Elemente in der Liste enthält, also gibt sietrue
für/some/prefix/to/my/path/one
sowiemy/path/one/and/some/suffix
oder sogar zurück/some/prefix/to/my/path/one/and/some/suffix
.Sogar
dummy/path/onerous
wird als Erfüllung der Kriterien angesehen, da escontains
die Zeichenfolge istmy/path/one
…quelle
Ja. Du hast recht. Ihr Stream-Ansatz hat einen gewissen Overhead. Sie können jedoch eine solche Konstruktion verwenden:
Der Hauptgrund für die Verwendung von Streams ist, dass sie Ihren Code einfacher und leichter lesbar machen.
quelle
anyMatch
eine Abkürzung fürfilter(...).findFirst().isPresent()
?Das Ziel von Streams in Java ist es, die Komplexität des Schreibens von parallelem Code zu vereinfachen. Es ist inspiriert von funktionaler Programmierung. Der serielle Stream dient nur dazu, den Code sauberer zu machen.
Wenn wir Leistung wollen, sollten wir parallelStream verwenden, das für entwickelt wurde. Die serielle ist im Allgemeinen langsamer.
Es gibt einen guten Artikel zu lesen über , und Leistung .
ForLoop
Stream
ParallelStream
In Ihrem Code können wir Beendigungsmethoden verwenden, um die Suche bei der ersten Übereinstimmung zu stoppen. (anyMatch ...)
quelle
Wie andere schon viele gute Punkte erwähnt haben, möchte ich nur die träge Bewertung in der Stream-Bewertung erwähnen . Wenn wir
map()
einen Stream mit Kleinbuchstaben erstellen, erstellen wir nicht sofort den gesamten Stream, sondern der Stream ist träge aufgebaut , weshalb die Leistung der herkömmlichen for-Schleife entsprechen sollte. Es wird kein vollständiger Scanvorgang durchgeführtmap()
undanyMatch()
gleichzeitig ausgeführt. SobaldanyMatch()
true zurückgegeben wird, wird es kurzgeschlossen.quelle