Java: tel het aantal woorden dat voorkomt in string

Bronknooppunt: 1719850

Introductie

Het tellen van het aantal woorden dat voorkomt in een string is een vrij gemakkelijke taak, maar er zijn verschillende manieren om dit te doen. U moet ook rekening houden met de efficiëntie van de methode, aangezien u doorgaans geautomatiseerde tools wilt gebruiken wanneer u geen handmatige arbeid wilt verrichten, bijvoorbeeld wanneer de zoekruimte groot is.

In deze handleiding leert u hoe u het aantal woordvoorkomens in een tekenreeks in Java kunt tellen:

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

We zoeken naar het aantal keren dat de targetWordgebruik String.split(), Collections.frequency() en reguliere expressies.

Tel voorvallen van woorden in string met Tekenreeks.split()

De eenvoudigste manier om het voorkomen van een doelwoord in een tekenreeks te tellen, is door de tekenreeks op elk woord te splitsen en door de array te herhalen, waarbij een wordCount op elke wedstrijd. Merk op dat wanneer een woord een soort interpunctie om zich heen heeft, zoals: wants. aan het einde van de zin - de eenvoudige splitsing op woordniveau zal correct worden behandeld wants en wants. als losse woorden!

Om dit te omzeilen, kun je eenvoudig alle interpunctie uit de zin verwijderen vaardigheden het splitsen:

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

In het for loop, itereren we gewoon door de array, waarbij we controleren of het element bij elke index gelijk is aan de targetWord. Als dat zo is, verhogen we de wordCount, die aan het einde van de uitvoering afdrukt:

2

Tel voorvallen van woorden in string met Collecties.frequentie()

De Collections.frequency() methode biedt een veel schonere implementatie op een hoger niveau, die een eenvoudig for lus, en controleert op beide identiteit (of het nu een object is een ander object) en gelijkheid (of een object gelijk is aan een ander object, afhankelijk van de kwalitatieve kenmerken van dat object).

De frequency() methode accepteert een lijst om door te zoeken, en het doelobject, en werkt ook voor alle andere objecten, waarbij het gedrag afhangt van hoe het object zelf implementeert equals(). In het geval van snaren, equals() cheques voor de inhoud van de string:


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

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

Hier hebben we de array geconverteerd die is verkregen uit split() in een Java ArrayList, met behulp van de helper asList() methode van de Arrays klas. De reductie operatie frequency() geeft een geheel getal terug dat de frequentie van . aangeeft targetWord in de lijst, en resulteert in:

2

Woordvoorkomens in String met Matcher (Reguliere expressies – RegEx)

Ten slotte kunt u reguliere expressies gebruiken om naar patronen te zoeken en het aantal overeenkomende patronen te tellen. Hier zijn reguliere expressies voor gemaakt, dus het past heel natuurlijk bij de taak. Op Java is de Pattern class wordt gebruikt om reguliere expressies weer te geven en te compileren, en de Matcher class wordt gebruikt om patronen te vinden en te matchen.

Met RegEx kunnen we de interpunctie-invariantie in de uitdrukking zelf coderen, dus het is niet nodig om de tekenreeks extern op te maken of interpunctie te verwijderen, wat de voorkeur heeft voor grote teksten waar het opslaan van een andere gewijzigde versie in het geheugen duur kan zijn:

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

Dit resulteert ook in:

2

Efficiëntiebenchmark

Dus, wat is het meest efficiënt? Laten we een kleine benchmark uitvoeren:

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

Elke methode wordt 100000 keer uitgevoerd (hoe hoger het getal, hoe lager de variantie en de resultaten op basis van toeval, vanwege de wet van de grote getallen). Het uitvoeren van deze code resulteert in:

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

Maar wat gebeurt er als we de zoekopdracht rekenkundig duurder maken door deze groter te maken? Laten we een synthetische zin genereren:

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

Dit maakt een string met de inhoud:

hello world hello world hello world hello ...

Bekijk onze praktische, praktische gids voor het leren van Git, met best-practices, door de industrie geaccepteerde normen en bijgevoegd spiekbriefje. Stop met Googlen op Git-commando's en eigenlijk leren het!

Als we nu naar "hallo" of "wereld" zouden zoeken, zouden er veel meer overeenkomsten zijn dan de twee van voorheen. Hoe doen onze methoden het nu in de benchmark?

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

Nu komt het splitsen van arrays het snelst uit! Over het algemeen zijn benchmarks afhankelijk van verschillende factoren, zoals de zoekruimte, het doelwoord, enz. en uw persoonlijke gebruiksscenario kan verschillen van de benchmark.

Advies: Probeer de methoden uit op uw eigen tekst, noteer de tijden en kies de meest efficiënte en elegante voor u.

Conclusie

In deze korte handleiding hebben we gekeken hoe u het aantal woorden voor een doelwoord in een tekenreeks in Java kunt tellen. We zijn begonnen door de string te splitsen en een eenvoudige teller te gebruiken, gevolgd door de Collections helper-klasse en tot slot het gebruik van reguliere expressies.

Uiteindelijk hebben we de methoden gebenchmarkt en vastgesteld dat de prestaties niet lineair zijn en afhankelijk zijn van de zoekruimte. Voor langere invoerteksten met veel overeenkomsten lijkt het splitsen van arrays het meest effectief. Probeer alle drie de methoden zelf uit en kies de meest performante.

Tijdstempel:

Meer van Stapelmisbruik