Java: Antal ord, der forekommer i streng

Kildeknude: 1719850

Introduktion

At tælle antallet af ordforekomster i en streng er en ret nem opgave, men der er flere måder at gøre det på. Du skal også tage højde for effektiviteten af ​​metoden, da du typisk vil bruge automatiserede værktøjer, når du ikke ønsker at udføre manuelt arbejde – altså når søgerummet er stort.

I denne vejledning lærer du, hvordan du tæller antallet af ordforekomster i en streng i Java:

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

Vi søger efter antallet af forekomster af targetWord, ved brug af String.split(), Collections.frequency() og regulære udtryk.

Tæl ordforekomster i streng med String.split()

Den enkleste måde at tælle forekomsten af ​​et målord i en streng på er at opdele strengen på hvert ord og iterere gennem arrayet, hvilket øger en wordCount på hver kamp. Bemærk, at når et ord har nogen form for tegnsætning omkring sig, som f.eks wants. i slutningen af ​​sætningen – vil den simple ord-niveauopdeling behandle korrekt wants , wants. som separate ord!

For at omgå dette kan du nemt fjerne al tegnsætning fra sætningen før opdele 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, itererer vi simpelthen gennem arrayet og kontrollerer, om elementet ved hvert indeks er lig med targetWord. Hvis det er det, øger vi wordCount, som i slutningen af ​​udførelsen udskriver:

2

Tæl ordforekomster i streng med Collections.frequency()

Collections.frequency() metode giver en meget renere implementering på højere niveau, som abstraherer en simpel for loop, og kontrollerer for både identitet (om et objekt is et andet objekt) og lighed (om et objekt er lig med et andet objekt, afhængigt af de kvalitative egenskaber ved det objekt).

frequency() metoden accepterer en liste, der skal søges igennem, og målobjektet og fungerer også for alle andre objekter, hvor adfærden afhænger af, hvordan objektet selv implementerer equals(). I tilfælde af strenge, equals() checks for indholdet af strengen:


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

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

Her har vi konverteret arrayet opnået fra split() ind i en Java ArrayList, ved hjælp af hjælperen asList() metode af Arrays klasse. Reduktionsoperationen frequency() returnerer et heltal, der angiver frekvensen af targetWord på listen og resulterer i:

2

Ordforekomster i streng med Matcher (regulære udtryk – RegEx)

Endelig kan du bruge regulære udtryk til at søge efter mønstre og tælle antallet af matchede mønstre. Regular Expressions er lavet til dette, så det passer meget naturligt til opgaven. I Java er Pattern klasse bruges til at repræsentere og kompilere regulære udtryk, og Matcher klasse bruges til at finde og matche mønstre.

Ved at bruge RegEx kan vi kode tegnsætningsinvariansen ind i selve udtrykket, så der er ingen grund til eksternt at formatere strengen eller fjerne tegnsætning, hvilket er at foretrække for store tekster, hvor det kan være dyrt at gemme en anden ændret version i hukommelsen:

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

Dette resulterer også i:

2

Benchmark for effektivitet

Så hvad er den mest effektive? Lad os køre et lille benchmark:

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

Hver metode vil blive kørt 100000 gange (jo højere tal, jo lavere varians og resultater på grund af tilfældigheder, på grund af loven om store tal). Kørsel af denne kode resulterer i:

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

Men hvad sker der, hvis vi gør søgningen mere beregningsmæssigt dyrere ved at gøre den større? Lad os generere en syntetisk sætning:

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

Dette skaber en streng med indholdet:

hello world hello world hello world hello ...

Tjek vores praktiske, praktiske guide til at lære Git, med bedste praksis, brancheaccepterede standarder og inkluderet snydeark. Stop med at google Git-kommandoer og faktisk lærer det!

Hvis vi nu skulle søge efter enten "hej" eller "verden" - ville der være mange flere kampe end de to fra før. Hvordan klarer vores metoder sig nu i benchmark?

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

Nu kommer array-opdeling hurtigst ud! Generelt afhænger benchmarks af forskellige faktorer – såsom søgeområdet, målordet osv., og din personlige brug kan være anderledes end benchmarken.

Rådgivning: Prøv metoderne på din egen tekst, noter tiderne, og vælg den mest effektive og elegante for dig.

Konklusion

I denne korte guide har vi taget et kig på, hvordan man tæller ordforekomster for et målord i en streng i Java. Vi har startet med at splitte strengen og bruge en simpel tæller, efterfulgt af at bruge Collections hjælperklasse og endelig ved at bruge regulære udtryk.

Til sidst har vi benchmarket metoderne og bemærket, at ydeevnen ikke er lineær og afhænger af søgeområdet. For længere inputtekster med mange matches synes opdeling af arrays at være den mest effektive. Prøv alle tre metoder på egen hånd, og vælg den mest effektive.

Tidsstempel:

Mere fra Stablemisbrug