Java: contar o número de ocorrências de palavras na string

Nó Fonte: 1719850

Introdução

Contar o número de ocorrências de palavras em uma string é uma tarefa bastante fácil, mas tem várias abordagens para isso. Você também deve levar em conta a eficiência do método, já que normalmente você deseja empregar ferramentas automatizadas quando não deseja realizar trabalho manual – ou seja, quando o espaço de pesquisa é grande.

Neste guia, você aprenderá a contar o número de ocorrências de palavras em uma string em Java:

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

Procuraremos o número de ocorrências do targetWord, Utilizando String.split(), Collections.frequency() e Expressões Regulares.

Contar ocorrências de palavras em string com String.split ()

A maneira mais simples de contar a ocorrência de uma palavra alvo em uma string é dividir a string em cada palavra e iterar pelo array, incrementando um wordCount em cada partida. Observe que quando uma palavra tem algum tipo de pontuação ao seu redor, como wants. no final da frase – a divisão simples no nível da palavra tratará corretamente wants e wants. como palavras separadas!

Para contornar isso, você pode remover facilmente toda a pontuação da frase antes dividindo-o:

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

No for loop, simplesmente iteramos pelo array, verificando se o elemento em cada índice é igual ao targetWord. Se for, incrementamos o wordCount, que ao final da execução imprime:

2

Contar ocorrências de palavras em string com Coleções.frequência()

A Collections.frequency() fornece uma implementação muito mais limpa e de alto nível, que abstrai uma simples for loop e verifica a identidade (se um objeto is outro objeto) e igualdade (se um objeto é igual a outro objeto, dependendo das características qualitativas desse objeto).

A frequency() O método aceita uma lista para pesquisar e o objeto de destino e também funciona para todos os outros objetos, onde o comportamento depende de como o próprio objeto implementa equals(). No caso das cordas, equals() verifica para o conteúdo da string:


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

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

Aqui, convertemos o array obtido de split() em um Java ArrayList, usando o auxiliar asList() método do Arrays classe. A operação de redução frequency() retorna um inteiro denotando a frequência de targetWord na lista e resulta em:

2

Ocorrências de palavras em String com Matcher (Expressões Regulares – RegEx)

Finalmente, você pode usar Expressões Regulares para pesquisar padrões e contar o número de padrões correspondentes. As Expressões Regulares são feitas para isso, então é muito natural para a tarefa. Em Java, o Pattern classe é usada para representar e compilar Expressões Regulares, e o Matcher A classe é usada para encontrar e combinar padrões.

Usando RegEx, podemos codificar a invariância de pontuação na própria expressão, então não há necessidade de formatar externamente a string ou remover a pontuação, o que é preferível para textos grandes onde armazenar outra versão alterada na memória pode ser caro:

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

Isso também resulta em:

2

Referência de eficiência

Então, qual é o mais eficiente? Vamos executar um pequeno 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));

Cada método será executado 100000 vezes (quanto maior o número, menor a variância e os resultados devido ao acaso, devido à lei dos grandes números). A execução deste código resulta em:

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

No entanto – o que acontece se tornarmos a pesquisa mais cara computacionalmente, tornando-a maior? Vamos gerar uma frase sintética:

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

Isso cria uma string com o conteúdo:

hello world hello world hello world hello ...

Confira nosso guia prático e prático para aprender Git, com práticas recomendadas, padrões aceitos pelo setor e folha de dicas incluída. Pare de pesquisar comandos Git no Google e realmente aprender -lo!

Agora, se procurássemos por “hello” ou “world” – haveria muito mais correspondências do que as duas de antes. Como nossos métodos se saem agora no benchmark?

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

Agora, a divisão de array sai mais rápido! Em geral, os benchmarks dependem de vários fatores – como o espaço de pesquisa, a palavra-alvo etc. e seu caso de uso pessoal pode ser diferente do benchmark.

Conselho: Experimente os métodos em seu próprio texto, observe os horários e escolha o mais eficiente e elegante para você.

Conclusão

Neste pequeno guia, vimos como contar ocorrências de palavras para uma palavra de destino, em uma string em Java. Começamos dividindo a string e usando um contador simples, seguido pelo Collections classe auxiliar e, finalmente, usando expressões regulares.

No final, comparamos os métodos e notamos que o desempenho não é linear e depende do espaço de pesquisa. Para textos de entrada mais longos com muitas correspondências, a divisão de matrizes parece ser a mais eficiente. Experimente todos os três métodos por conta própria e escolha o mais eficiente.

Carimbo de hora:

Mais de Abuso de pilha