Ich muss viele verschiedene Unterzeichenfolgen in einer Zeichenfolge auf die effizienteste Weise ersetzen. Gibt es einen anderen Weg als den Brute-Force-Weg, um jedes Feld mit string.replace zu ersetzen?
Wenn die Zeichenfolge, mit der Sie arbeiten, sehr lang ist oder Sie mit vielen Zeichenfolgen arbeiten, kann es sich lohnen, einen java.util.regex.Matcher zu verwenden (dies erfordert Zeit im Voraus zum Kompilieren, sodass es nicht effizient ist wenn Ihre Eingabe sehr klein ist oder sich Ihr Suchmuster häufig ändert).
Unten finden Sie ein vollständiges Beispiel, das auf einer Liste von Token basiert, die einer Karte entnommen wurden. (Verwendet StringUtils von Apache Commons Lang).
Map<String,String> tokens = new HashMap<String,String>();
tokens.put("cat", "Garfield");
tokens.put("beverage", "coffee");
String template = "%cat% really needs some %beverage%.";
// Create pattern of the format "%(cat|beverage)%"
String patternString = "%(" + StringUtils.join(tokens.keySet(), "|") + ")%";
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(template);
StringBuffer sb = new StringBuffer();
while(matcher.find()) {
matcher.appendReplacement(sb, tokens.get(matcher.group(1)));
}
matcher.appendTail(sb);
System.out.println(sb.toString());
Sobald der reguläre Ausdruck kompiliert ist, ist das Scannen der Eingabezeichenfolge im Allgemeinen sehr schnell (obwohl Sie, wenn Ihr regulärer Ausdruck komplex ist oder ein Backtracking beinhaltet, immer noch einen Benchmark durchführen müssen, um dies zu bestätigen!).
"%(" + StringUtils.join(tokens.keySet(), "|") + ")%";
Algorithmus
Eine der effizientesten Möglichkeiten, übereinstimmende Zeichenfolgen (ohne reguläre Ausdrücke) zu ersetzen, besteht darin, den Aho-Corasick-Algorithmus durch einen performanten Trie (ausgesprochen "try"), einen schnellen Hashing- Algorithmus und eine effiziente Implementierung von Sammlungen zu verwenden .
Einfacher Code
Eine einfache Lösung nutzt Apache
StringUtils.replaceEach
wie folgt:Dies verlangsamt große Texte.
Schneller Code
Bors Implementierung des Aho-Corasick-Algorithmus führt zu einer etwas höheren Komplexität, die durch die Verwendung einer Fassade mit derselben Methodensignatur zu einem Implementierungsdetail wird:
Benchmarks
Für die Benchmarks wurde der Puffer mit randomNumeric wie folgt erstellt:
Wo bestimmt
MATCHES_DIVISOR
die Anzahl der zu injizierenden Variablen:Der Benchmark-Code selbst ( JMH schien übertrieben):
1.000.000: 1.000
Ein einfacher Mikro-Benchmark mit 1.000.000 Zeichen und 1.000 zufällig platzierten Zeichenfolgen, die ersetzt werden müssen.
Kein Wettbewerb.
10.000: 1.000
Verwenden Sie 10.000 Zeichen und 1.000 übereinstimmende Zeichenfolgen, um Folgendes zu ersetzen:
Die Kluft schließt sich.
1.000: 10
Verwenden Sie 1.000 Zeichen und 10 übereinstimmende Zeichenfolgen, um Folgendes zu ersetzen:
Bei kurzen Saiten übertrifft der Aufwand für die Einrichtung von Aho-Corasick den Brute-Force-Ansatz um
StringUtils.replaceEach
.Ein hybrider Ansatz basierend auf der Textlänge ist möglich, um das Beste aus beiden Implementierungen herauszuholen.
Implementierungen
Vergleichen Sie andere Implementierungen für Text, der länger als 1 MB ist, einschließlich:
Papiere
Artikel und Informationen zum Algorithmus:
quelle
Das hat bei mir funktioniert:
Beispiel:
Ausgabe: Apfel-Banane-Frucht-
quelle
Wenn Sie einen String mehrmals ändern, ist es normalerweise effizienter, einen StringBuilder zu verwenden (aber messen Sie Ihre Leistung, um dies herauszufinden) :
Jedes Mal, wenn Sie einen String ersetzen, wird ein neues String-Objekt erstellt, da Strings unveränderlich sind. StringBuilder ist veränderbar, dh es kann beliebig geändert werden.
quelle
StringBuilder
wird das Ersetzen effizienter durchführen, da sein Zeichenarray-Puffer auf eine erforderliche Länge angegeben werden kann.StringBuilder
ist mehr als nur zum Anhängen gedacht!Die eigentliche Frage ist natürlich, ob dies eine zu weit gehende Optimierung ist. Die JVM kann sehr gut mit der Erstellung mehrerer Objekte und der anschließenden Speicherbereinigung umgehen. Wie bei allen Optimierungsfragen ist meine erste Frage, ob Sie dies gemessen und festgestellt haben, dass es sich um ein Problem handelt.
quelle
Wie wäre es mit der replaceAll () -Methode?
quelle
str.replaceAll(search1, replace1).replaceAll(search2, replace2).replaceAll(search3, replace3).replaceAll(search4, replace4)
Rythm ist eine Java-Template-Engine, die jetzt mit einer neuen Funktion namens String-Interpolationsmodus veröffentlicht wurde, mit der Sie Folgendes tun können:
Der obige Fall zeigt, dass Sie Argumente nach Position an die Vorlage übergeben können. Mit Rythm können Sie Argumente auch nach Namen übergeben:
Hinweis Rythm ist SEHR SCHNELL, etwa zwei- bis dreimal schneller als String.format und Geschwindigkeit, da die Vorlage in Java-Bytecode kompiliert wird und die Laufzeitleistung der Konzentration mit StringBuilder sehr nahe kommt.
Links:
quelle
"%cat% really needs some %beverage%.";
dieses%
getrennte Token nicht ein vordefiniertes Format? Ihr erster Punkt ist noch lustiger, JDK bietet viele "alte Fähigkeiten", einige davon beginnen in den 90ern, warum machen sich die Leute die Mühe, sie zu benutzen? Ihre Kommentare und Abstimmungen machen keinen wirklichen SinnDas Folgende basiert auf der Antwort von Todd Owen . Diese Lösung hat das Problem, dass unerwartete Ergebnisse erzielt werden können, wenn die Ersetzungen Zeichen enthalten, die in regulären Ausdrücken eine besondere Bedeutung haben. Ich wollte auch in der Lage sein, optional eine Suche ohne Berücksichtigung der Groß- und Kleinschreibung durchzuführen. Folgendes habe ich mir ausgedacht:
Hier sind meine Unit-Testfälle:
quelle
quelle
Überprüfen Sie dies:
Zum Beispiel:
quelle
Zusammenfassung: Implementierung von Daves Antwort in einer Klasse, um automatisch den effizientesten der beiden Algorithmen auszuwählen.
Dies ist eine vollständige Implementierung in einer Klasse, die auf der oben genannten hervorragenden Antwort von Dave Jarvis basiert . Die Klasse wählt automatisch zwischen den beiden verschiedenen bereitgestellten Algorithmen, um maximale Effizienz zu erzielen. (Diese Antwort ist für Personen gedacht, die nur schnell kopieren und einfügen möchten.)
ReplaceStrings-Klasse:
Benötigte Maven-Abhängigkeiten:
(Fügen Sie diese bei Bedarf Ihrer POM-Datei hinzu.)
quelle