it-swarm.com.de

String replaceAll () vs. Matcher replaceAll () (Leistungsunterschiede)

Ziemlich einfache Frage, aber dies kommt von einer C/C++ - Person, die sich mit den Feinheiten von Java auseinandersetzt.

Ich verstehe, dass ich jUnit und einige eigene Leistungstests starten kann, um eine Antwort zu erhalten. aber ich frage mich nur, ob das da draußen ist.

Gibt es bekannte Unterschiede zwischen String.replaceAll () und Matcher.replaceAll () (für ein Matcher-Objekt, das aus einem Regex.Pattern erstellt wurde) in Bezug auf die Leistung?

Was sind auch die High-Level-API-Unterschiede zwischen den beiden? (Unveränderlichkeit, Umgang mit NULLs, Umgang mit leeren Strings, Kaffeezubereitung usw.)

42
Suvesh Pratapa

Gemäß der Dokumentation für String.replaceAll hat es folgende Angaben zum Aufruf der Methode zu sagen:

Ein Aufruf dieser Methode der Formular str.replaceAll(regex, repl) liefert genau das gleiche Ergebnis wie die Ausdruck

Pattern.compile(regex).matcher(str).replaceAll(repl)

Daher kann erwartet werden, dass die Leistung zwischen dem Aufruf von String.replaceAll und dem expliziten Erstellen einer Matcher und Pattern die gleiche ist.

Bearbeiten

Wie in den Kommentaren darauf hingewiesen wurde, wäre der nicht vorhandene Leistungsunterschied für einen einzelnen Aufruf von replaceAll oder String für Matcher zutreffend. Wenn jedoch mehrere Aufrufe von replaceAll ausgeführt werden müssen, könnte dies als vorteilhaft angesehen werden halten Sie eine kompilierte Pattern, so dass die relativ teure Kompilierung von regulären Ausdrücken nicht jedes Mal durchgeführt werden muss.

71
coobird

Quellcode von String.replaceAll():

public String replaceAll(String regex, String replacement) {
    return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}

Das Pattern muss zuerst kompiliert werden - wenn Sie es viele Male mit dem gleichen Pattern auf kurzen Strings ausführen, wird die Leistung deutlich besser, wenn Sie ein kompiliertes Pattern wiederverwenden.

22

Der Hauptunterschied besteht darin, dass Sie, wenn Sie sich an die Pattern halten, die zum Erzeugen der Matcher verwendet wird, vermeiden können, den Regex jedes Mal neu zu kompilieren, wenn Sie ihn verwenden. Beim Durchlaufen von String erhalten Sie nicht die Möglichkeit, so zu "cachen".

Wenn Sie jedes Mal einen anderen regulären Ausdruck haben, ist die Verwendung der Variablen String der Klasse replaceAll in Ordnung. Wenn Sie denselben Ausdruck auf viele Zeichenfolgen anwenden, erstellen Sie eine Pattern und verwenden Sie sie erneut.

9
erickson

Unveränderlichkeit/Thread-Sicherheit: Kompilierte Patterns sind unveränderlich, Matchers nicht. (Siehe Ist Java Regex Thread Safe? )

Leere Zeichenfolgen behandeln: replaceAll sollte leere Zeichenfolgen ordnungsgemäß behandeln (es entspricht keinem leeren Eingabezeichenfolgenmuster)

Kaffee zubereiten, etc .: das letzte was ich gehört habe, weder String noch Pattern oder Matcher hatten API-Funktionen dafür.

edit: Was NULLs angeht, so wird dies in der Dokumentation für String und Pattern nicht explizit erwähnt, aber ich vermute, sie würden eine NullPointerException auslösen, da sie einen String erwarten.

6
Jason S

Der Unterschied besteht darin, dass String.replaceAll () die Regex bei jedem Aufruf kompiliert. Es gibt keine Entsprechung für die statische Regex.Replace () - Methode von .NET, die die kompilierte Regex automatisch zwischenspeichert. Normalerweise ist replaceAll () ein Vorgang, den Sie nur einmal ausführen. Wenn Sie ihn jedoch wiederholt mit demselben Regex aufrufen, insbesondere in einer Schleife, sollten Sie ein Musterobjekt erstellen und die Matcher-Methode verwenden.

Sie können den Matcher auch vorzeitig erstellen und ihn mit seiner reset () -Methode für jede Verwendung neu ausrichten:

Matcher m = Pattern.compile(regex).matcher("");
for (String s : targets)
{
  System.out.println(m.reset(s).replaceAll(repl));
}

Der Leistungsvorteil der Wiederverwendung des Matchers ist natürlich nirgends so groß wie der der Wiederverwendung des Patterns.

4
Alan Moore

Die Implementierung von String.replaceAll zeigt Ihnen alles, was Sie wissen müssen:

return Pattern.compile(regex).matcher(this).replaceAll(replacement);

(Und die Docs sagen dasselbe.)

Ich habe zwar noch nicht nach Caching gesucht, aber sicherlich würde ich erwarten, dass das Kompilieren eines Patterns Once und das Beibehalten eines statischen Verweises effizienter ist als der Aufruf von Pattern.compile mit dem gleichen Pattern. Wenn es einen Cache gibt, bedeutet dies eine kleine Effizienzersparnis - wenn nicht, könnte es eine große sein.

4
Jon Skeet

Die anderen Antworten decken den Leistungsteil des OP ausreichend ab, aber ein weiterer Unterschied zwischen Matcher::replaceAll und String::replaceAll ist auch ein Grund, Ihre eigene Pattern zu kompilieren. Wenn Sie eine Variable Pattern selbst kompilieren, gibt es Optionen wie Flags, um die Anwendung des Regex zu ändern. Zum Beispiel:

Pattern myPattern = Pattern.compile(myRegex, Pattern.CASE_INSENSITIVE);

Die Variable Matcher wendet alle Flags an, die Sie beim Aufruf von Matcher::replaceAll setzen.

Es gibt auch andere Flags, die Sie setzen können. Meistens wollte ich nur darauf hinweisen, dass die Pattern- und Matcher-API viele Optionen hat, und das ist der Hauptgrund, über den einfachen String::replaceAll hinauszugehen.

0
Indigenuity