Java: Antal ord som förekommer i sträng

Beskrivning

Att räkna antalet ordförekomster i en sträng är en ganska enkel uppgift, men det finns flera sätt att göra det på. Du måste också ta hänsyn till metodens effektivitet, eftersom du vanligtvis vill använda automatiserade verktyg när du inte vill utföra manuellt arbete – dvs när sökutrymmet är stort.

I den här guiden lär du dig hur du räknar antalet ordförekomster i en sträng i Java:

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

Vi kommer att söka efter antalet förekomster av targetWord, Med användning av String.split(), Collections.frequency() och reguljära uttryck.

Räkna ordförekomster i sträng med String.split ()

Det enklaste sättet att räkna förekomsten av ett målord i en sträng är att dela strängen på varje ord och iterera genom arrayen, öka en wordCount på varje match. Observera att när ett ord har någon form av skiljetecken runt sig, som t.ex wants. i slutet av meningen – den enkla ordnivåuppdelningen kommer att behandlas korrekt wants och wants. som separata ord!

För att komma runt detta kan du enkelt ta bort alla skiljetecken från meningen innan dela upp det:

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);

I for loop, itererar vi helt enkelt genom arrayen och kontrollerar om elementet vid varje index är lika med targetWord. Om det är det, ökar vi wordCount, som i slutet av utförandet skriver ut:

2

Räkna ordförekomster i sträng med Collections.frequency()

Smakämnen Collections.frequency() metod ger en mycket renare implementering på högre nivå, som abstraherar bort en enkel for loop, och kontrollerar både identitet (om ett objekt is ett annat objekt) och likhet (om ett objekt är lika med ett annat objekt, beroende på det objektets kvalitativa egenskaper).

Smakämnen frequency() metoden accepterar en lista att söka igenom, och målobjektet, och fungerar även för alla andra objekt, där beteendet beror på hur objektet självt implementerar equals(). När det gäller strängar, equals() kontrollerar för innehållet i strängen:


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

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

Här har vi konverterat arrayen som erhålls från split() till en Java ArrayList, med hjälp av hjälparen asList() metod för Arrays klass. Reduktionsoperationen frequency() returnerar ett heltal som anger frekvensen av targetWord i listan och resulterar i:

2

Ordförekomster i sträng med Matchning (Reguljära uttryck – RegEx)

Slutligen kan du använda reguljära uttryck för att söka efter mönster och räkna antalet matchade mönster. Regular Expressions är gjorda för detta, så det passar väldigt naturligt för uppgiften. I Java är Pattern klass används för att representera och kompilera reguljära uttryck, och Matcher klass används för att hitta och matcha mönster.

Med RegEx kan vi koda interpunktioninvariansen i själva uttrycket, så det finns inget behov av att externt formatera strängen eller ta bort interpunktion, vilket är att föredra för stora texter där det kan vara dyrt att lagra en annan ändrad version i minnet:

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);

Detta resulterar också i:

2

Effektivitetsriktmärke

Så vilken är den mest effektiva? Låt oss köra ett litet riktmärke:

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));

Varje metod kommer att köras 100000 XNUMX gånger (ju högre siffra, desto lägre varians och resultat på grund av slumpen, på grund av lagen om stora siffror). Att köra den här koden resulterar i:

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

Men vad händer om vi gör sökningen beräkningsmässigt dyrare genom att göra den större? Låt oss skapa en syntetisk mening:

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);

Detta skapar en sträng med innehållet:

hello world hello world hello world hello ...

Kolla in vår praktiska, praktiska guide för att lära dig Git, med bästa praxis, branschaccepterade standarder och medföljande fuskblad. Sluta googla Git-kommandon och faktiskt lära Det!

Om vi ​​nu skulle söka efter antingen "hej" eller "värld" - skulle det finnas många fler matchningar än de två från tidigare. Hur gör våra metoder nu i riktmärket?

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

Nu kommer array splitting ut snabbast! I allmänhet beror riktmärken på olika faktorer – som sökutrymmet, målordet etc. och ditt personliga användningsfall kan skilja sig från riktmärket.

Råd: Prova metoderna på din egen text, notera tiderna och välj den mest effektiva och eleganta för dig.

Slutsats

I den här korta guiden har vi tagit en titt på hur man räknar ordförekomster för ett målord, i en sträng i Java. Vi har börjat med att dela strängen och använda en enkel räknare, följt av att använda Collections hjälparklass och slutligen med hjälp av reguljära uttryck.

Till slut har vi benchmarkat metoderna och noterat att prestandan inte är linjär och beror på sökutrymmet. För längre inmatningstexter med många matchningar verkar splittrad array vara den mest presterande. Prova alla tre metoderna på egen hand och välj den mest presterande.

Tidsstämpel:

Mer från Stackabuse