Java: Anzahl des Wortvorkommens im String zählen

Einleitung

Das Zählen der Wortvorkommen in einer Zeichenfolge ist eine ziemlich einfache Aufgabe, es gibt jedoch mehrere Ansätze, dies zu tun. Sie müssen auch die Effizienz der Methode berücksichtigen, da Sie typischerweise automatisierte Tools einsetzen möchten, wenn Sie keine manuelle Arbeit leisten möchten – dh wenn der Suchraum groß ist.

In dieser Anleitung erfahren Sie, wie Sie die Anzahl der Wortvorkommen in einer Zeichenfolge in Java zählen:

String searchText = "Your body may be chrome, but the heart never changes. It wants what it wants.";
String targetWord = "wants";

Wir suchen nach der Anzahl der Vorkommen von targetWordunter Verwendung von String.split(), Collections.frequency() und Reguläre Ausdrücke.

Wortvorkommen im String zählen mit String.split()

Der einfachste Weg, das Vorkommen eines Zielworts in einer Zeichenfolge zu zählen, besteht darin, die Zeichenfolge für jedes Wort aufzuteilen und das Array zu durchlaufen, wobei a erhöht wird wordCount bei jedem Spiel. Beachten Sie, dass, wenn ein Wort irgendeine Art von Interpunktion umgibt, wie z wants. am Ende des Satzes – die einfache Trennung auf Wortebene wird korrekt behandelt wants machen wants. als getrennte Wörter!

Um dies zu umgehen, können Sie ganz einfach alle Satzzeichen aus dem Satz entfernen Bevor aufteilen:

String[] words = searchText.replaceAll("p{Punct}", "").split(" ");

int wordCount = 0;
for (int i=0; i < words.length; i++)
    if (words[i].equals(targetWord))
        wordCount++;
System.out.println(wordCount);

Im for Schleife durchlaufen wir einfach das Array und prüfen, ob das Element an jedem Index gleich dem ist targetWord. Wenn ja, erhöhen wir die wordCount, die am Ende der Ausführung Folgendes ausgibt:

2

Wortvorkommen im String zählen mit Sammlungen.frequenz()

Das Collections.frequency() -Methode bietet eine viel sauberere Implementierung auf höherer Ebene, die eine einfache abstrahiert for -Schleife und überprüft beide Identitäten (ob ein Objekt is ein anderes Objekt) und Gleichheit (ob ein Objekt einem anderen Objekt gleicht, abhängig von den qualitativen Merkmalen dieses Objekts).

Das frequency() -Methode akzeptiert eine zu durchsuchende Liste und das Zielobjekt und funktioniert auch für alle anderen Objekte, wobei das Verhalten davon abhängt, wie das Objekt selbst implementiert wird equals(). Bei Saiten ist equals() Schecks für die Inhalt der Zeichenfolge:


searchText = searchText.replaceAll("p{Punct}", "");

int wordCount = Collections.frequency(Arrays.asList(searchText.split(" ")), targetWord);
System.out.println(wordCount);

Hier haben wir das erhaltene Array konvertiert split() in ein Java ArrayList, mit dem Helfer asList() Methode der Arrays Klasse. Die Reduktionsoperation frequency() gibt eine ganze Zahl zurück, die die Häufigkeit von angibt targetWord in der Liste und führt zu:

2

Wortvorkommen in Zeichenfolgen mit Matcher (reguläre Ausdrücke – RegEx)

Schließlich können Sie reguläre Ausdrücke verwenden, um nach Mustern zu suchen und die Anzahl der übereinstimmenden Muster zu zählen. Reguläre Ausdrücke sind dafür gemacht, also ist es eine sehr natürliche Lösung für die Aufgabe. Auf Java ist die Pattern Klasse wird verwendet, um reguläre Ausdrücke darzustellen und zu kompilieren, und die Matcher Klasse wird verwendet, um Muster zu finden und abzugleichen.

Mit RegEx können wir die Interpunktionsinvarianz in den Ausdruck selbst codieren, sodass die Zeichenfolge nicht extern formatiert oder Interpunktion entfernt werden muss, was für große Texte vorzuziehen ist, bei denen das Speichern einer anderen geänderten Version im Speicher teuer sein könnte:

Pattern pattern = Pattern.compile("b%s(?!w)".format(targetWord));

Pattern pattern = Pattern.compile("bwants(?!w)");
Matcher matcher = pattern.matcher(searchText);

int wordCount = 0;
while (matcher.find())
    wordCount++;

System.out.println(wordCount);

Dies führt auch zu:

2

Effizienz-Benchmark

Was ist also am effizientesten? Lassen Sie uns einen kleinen Benchmark durchführen:

int runs = 100000;

long start1 = System.currentTimeMillis();
for (int i = 0; i < runs; i++) {
    int result = countOccurencesWithSplit(searchText, targetWord);
}

long end1 = System.currentTimeMillis();
System.out.println(String.format("Array split approach took: %s miliseconds", end1-start1));

long start2 = System.currentTimeMillis();
  for (int i = 0; i < runs; i++) {
    int result = countOccurencesWithCollections(searchText, targetWord);
}

long end2 = System.currentTimeMillis();
System.out.println(String.format("Collections.frequency() approach took: %s miliseconds", end2-start2));

long start3 = System.currentTimeMillis();
for (int i = 0; i < runs; i++) {
    int result = countOccurencesWithRegex(searchText, targetWord);
}

long end3 = System.currentTimeMillis();
System.out.println(String.format("Regex approach took: %s miliseconds", end3-start3));

Jede Methode wird 100000 Mal durchlaufen (je höher die Zahl, desto geringer die Varianz und die zufälligen Ergebnisse aufgrund des Gesetzes der großen Zahlen). Das Ausführen dieses Codes führt zu:

Array split approach took: 152 miliseconds
Collections.frequency() approach took: 140 miliseconds
Regex approach took: 92 miliseconds

Was passiert jedoch, wenn wir die Suche rechenintensiver machen, indem wir sie vergrößern? Lassen Sie uns einen synthetischen Satz generieren:

List possibleWords = Arrays.asList("hello", "world ");
StringBuffer searchTextBuffer = new StringBuffer();

for (int i = 0; i < 100; i++) {
    searchTextBuffer.append(String.join(" ", possibleWords));
}
System.out.println(searchTextBuffer);

Dadurch wird eine Zeichenfolge mit dem Inhalt erstellt:

hello world hello world hello world hello ...

Sehen Sie sich unseren praxisnahen, praktischen Leitfaden zum Erlernen von Git an, mit Best Practices, branchenweit akzeptierten Standards und einem mitgelieferten Spickzettel. Hören Sie auf, Git-Befehle zu googeln und tatsächlich in Verbindung, um es!

Wenn wir jetzt entweder nach „Hallo“ oder „Welt“ suchen würden, gäbe es viel mehr Übereinstimmungen als die beiden zuvor. Wie schneiden unsere Methoden nun im Benchmark ab?

Array split approach took: 606 miliseconds
Collections.frequency() approach took: 899 miliseconds
Regex approach took: 801 miliseconds

Jetzt kommt Array-Splitting am schnellsten heraus! Generell hängen Benchmarks von verschiedenen Faktoren ab – wie dem Suchraum, dem Zielwort etc. und Ihr persönlicher Anwendungsfall kann sich vom Benchmark unterscheiden.

Hinweis: Probieren Sie die Methoden an Ihrem eigenen Text aus, notieren Sie sich die Zeiten und wählen Sie die effizienteste und eleganteste für Sie aus.

Zusammenfassung

In dieser kurzen Anleitung haben wir uns angesehen, wie man Wortvorkommen für ein Zielwort in einer Zeichenfolge in Java zählt. Wir haben damit begonnen, die Zeichenfolge aufzuteilen und einen einfachen Zähler zu verwenden, gefolgt von der Verwendung von Collections Hilfsklasse und schließlich die Verwendung regulärer Ausdrücke.

Am Ende haben wir die Methoden einem Benchmarking unterzogen und festgestellt, dass die Leistung nicht linear ist und vom Suchraum abhängt. Für längere Eingabetexte mit vielen Übereinstimmungen scheint das Aufteilen von Arrays am performantesten zu sein. Probieren Sie alle drei Methoden selbst aus und wählen Sie die leistungsstärkste aus.

Zeitstempel:

Mehr von Stapelmissbrauch