Überblick über die Leistung regulärer Ausdrücke in Java

Ein Überblick über die Leistung regulärer Ausdrücke in Java

1. Überblick

In diesem kurzen Tutorial zeigen wir, wie die Pattern-Matching-Engine funktioniert. Wir werden auch verschiedene Möglichkeiten zur Optimierung vonregular expressions in Java vorstellen.

Eine Einführung in die Verwendung vonregular expressions finden Sie unterto this article here.

2. Die Pattern-Matching-Engine

Dasjava.util.regex-Paket verwendet eine Art Mustervergleichs-Engine namensNondeterministic Finite Automaton (NFA). It’s considered nondeterministic because while trying to match a regular expression on a given string, each character in the input might be checked several times against different parts of the regular expression.

Im Hintergrund verwendet der oben erwähnte Motorbacktracking. Dieser allgemeine Algorithmus versucht, alle Möglichkeiten auszuschöpfen, bis er einen Fehler deklariert. Betrachten Sie das folgende Beispiel, um dieNFA besser zu verstehen:

"tra(vel|ce|de)m"

Mit der EingabeStringtravel“ sucht der Motor zuerst nach „tra“ und findet sie sofort.

Danach wird versucht, ab dem vierten Zeichen mit "vel" übereinzustimmen. Dies wird übereinstimmen, also wird es vorwärts gehen und versuchen, mit „m“ übereinzustimmen.

Das stimmt nicht überein, und aus diesem Grund wird zum vierten Zeichen zurückgekehrt und nach "ce" gesucht. Auch dies wird nicht übereinstimmen, daher wird es wieder auf die vierte Position zurückkehren und es mit "de" versuchen. Diese Zeichenfolge stimmt auch nicht überein. Daher wird zum zweiten Zeichen in der Eingabezeichenfolge zurückgekehrt und versucht, nach weiteren "tra" zu suchen.

Beim letzten Fehler gibt der Algorithmus einen Fehler zurück.

Mit dem einfachen letzten Beispiel musste die Engine mehrmals zurückverfolgen, während versucht wurde, die EingabeString mit dem regulären Ausdruck abzugleichen. Aus diesem Grundit’s important to minimize the amount of backtracking that it does.

3. Möglichkeiten zur Optimierung vonRegular Expressions

3.1. Vermeiden Sie eine Neukompilierung

Reguläre Ausdrücke in Java werden in eine interne Datenstruktur kompiliert. Diese Zusammenstellung ist zeitaufwändig.

Jedes Mal, wenn wir dieString.matches(String regex) -Smethod aufrufen, wird der angegebene reguläre Ausdruck neu kompiliert:

if (input.matches(regexPattern)) {
    // do something
}

Wie wir sehen können, wird jedes Mal, wenn die Bedingung ausgewertet wird, der reguläre Ausdruck kompiliert.

Zur Optimierung ist es möglich, zuerst das Muster zu kompilieren und dann einMatcherzu erstellen, um die Übereinstimmungen im Wert zu finden:

Pattern pattern = Pattern.compile(regexPattern);
for(String value : values) {
    Matcher matcher = pattern.matcher(value);
    if (matcher.matches()) {
        // do something
    }
}

Eine Alternative zur obigen Optimierung ist die Verwendung derselbenMatcher-Instanz mit ihrerreset()-Methode:

Pattern pattern = Pattern.compile(regexPattern);
Matcher matcher = pattern.matcher("");
for(String value : values) {
    matcher.reset(value);
    if (matcher.matches()) {
      // do something
    }
}

DaMatcher nicht threadsicher ist, müssen wir bei der Verwendung dieser Variante vorsichtig sein. In Multithread-Szenarien kann dies wahrscheinlich gefährlich sein.

Zusammenfassend lässt sich sagen, dass es in jeder Situation, in der wir sicher sind, dass zu jedem Zeitpunkt nur ein Benutzer derMatcher vorhanden ist, in Ordnung ist, sie mitreset wiederzuverwenden. Im Übrigen reicht es aus, das vorkompilierte wiederzuverwenden.

3.2. Mit Alternation arbeiten

Wie wir gerade im letzten Abschnitt überprüft haben, kann die unzureichende Verwendung von Abwechslungen die Leistung beeinträchtigen. Es ist wichtig, Optionen zu platzieren, die eher im Vordergrund stehen, damit sie schneller angepasst werden können.

Außerdem müssen wir gemeinsame Muster zwischen ihnen extrahieren. Es ist nicht dasselbe zu sagen:

(travel | trade | trace)

Als:

tra(vel | de | ce)

Letzteres ist schneller, da dieNFA versuchen, mit "tra" übereinzustimmen, und keine der Alternativen ausprobieren, wenn sie nicht gefunden werden.

3.3. Gruppen erfassen

Jedes Mal, wenn wir Gruppen erfassen, wird eine kleine Zeitstrafe verhängt.

Wenn wir den Text nicht innerhalb einer Gruppe erfassen müssen, sollten wir die Verwendung von nicht erfassbaren Gruppen in Betracht ziehen. Verwenden Sie statt „(M)“ auch „(?:M)“.

4. Fazit

In diesem kurzen Artikel haben wir kurz überarbeitet, wieNFA funktioniert. Anschließend untersuchten wir, wie wir die Leistung unserer regulären Ausdrücke optimieren können, indem wir unsere Muster vorkompilieren und aMatcher wiederverwenden.

Schließlich haben wir einige Überlegungen angestellt, die bei der Arbeit mit Alternativen und Gruppen zu berücksichtigen sind.

Wie üblich kann der vollständige Quellcodeover on GitHub gefunden werden.