Verifique se uma string contém uma subcadeia

Verifique se uma string contém uma subcadeia

1. Visão geral

Neste tutorial, revisaremos várias maneiras de verificar se umString contém uma substring e compararemos o desempenho de cada uma.

2. String.indexOf

Vamos primeiro tentar usar o métodoString.indexOf. indexOf gives us the first position where the substring is found, or -1 if it isn’t found at all.

Quando procurarmos por "Rhap", ele retornará 9:

Assert.assertEquals(9, "Bohemian Rhapsodyan".indexOf("Rhap"));

Quando pesquisamos por “rhap”,it’ll return -1 because it’s case sensitive.

Assert.assertEquals(-1, "Bohemian Rhapsodyan".indexOf("rhap"));
Assert.assertEquals(9, "Bohemian Rhapsodyan".toLowerCase().indexOf("rhap"));

Também é importante observar que se pesquisarmossubstring “an”, ele retornará 6 porque retorna a primeira ocorrência:

Assert.assertEquals(6, "Bohemian Rhapsodyan".indexOf("an"));

3. String.contains

A seguir, vamos tentarString.contains. contains will search a substring throughout the entire String and will return true if it’s found and false otherwise.

Neste exemplo,contains retornatrue porque “Hey” foi encontrado.

Assert.assertTrue("Hey Ho, let's go".contains("Hey"));

Se a string não for encontrada,contains retornafalse:

Assert.assertFalse("Hey Ho, let's go".contains("jey"));

No último exemplo, “hey” não foi encontrado porqueString.contains is case-sensitive.

Assert.assertFalse("Hey Ho, let's go".contains("hey"));
Assert.assertTrue("Hey Ho, let's go".toLowerCase().contains("hey"));

Um ponto interessante é quecontains internally calls indexOf para saber se umsubstring está contido, ou não.

4. StringUtils.containsIgnoreCase

Nossa terceira abordagem usaráStringUtils#containsIgnoreCase from the Apache Commons Lang library:

Assert.assertTrue(StringUtils.containsIgnoreCase("Runaway train", "train"));
Assert.assertTrue(StringUtils.containsIgnoreCase("Runaway train", "Train"));

Podemos ver que éwill check if a substring is contained in a String, ignoring the case. É por isso quecontainsIgnoreCase retornatrue quando pesquisamos por "Trai" e também "trai" dentro de "Trem em fuga".

Essa abordagemwon’t be as efficient as the previous approaches, pois leva mais tempo para ignorar o caso. containsIgnoreCase converte internamente todas as letras em maiúsculas e compara as letras convertidas em vez das originais.

5. UsandoPattern

Nossa última abordagem usará umPattern with a regular expression:

Pattern pattern = Pattern.compile("(?

Podemos observar quewe need to build the Pattern first, then we need to create the Matcher, and finally, we can check with the find method if there’s an occurrence of the substring or not:

Matcher matcher = pattern.matcher("Hit the road Jack");
Assert.assertTrue(matcher.find());

Por exemplo, a primeira vez quefind é executado, ele retornatrue porque a palavra "estrada" está contida dentro da string "Pegue a estrada Jack", mas quando tentamos encontrar a mesma palavra na string “e não volte mais” retornafalse:

Matcher matcher = pattern.matcher("and don't you come back no more");
Assert.assertFalse(matcher.find());

6. Comparação de desempenho

Usaremos uma estrutura de micro-benchmark de código aberto chamadaJava Microbenchmark Harness (JMH) para decidir qual método é o mais eficiente em termos de tempo de execução.

6.1. Configuração de benchmark

Como em todo benchmark JMH, temos a capacidade de escrever um métodosetup, a fim de ter certas coisas no lugar antes de nossos benchmarks serem executados:

@Setup
public void setup() {
    message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, " +
      "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
      "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris " +
      "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in " +
      "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " +
      "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " +
      "mollit anim id est laborum";
    pattern = Pattern.compile("(?

No métodosetup, estamos inicializando o campomessage. Usaremos isso como o texto-fonte para nossas várias implementações de pesquisa.

Também estamos inicializandopattern para usá-lo posteriormente em um de nossos benchmarks.

6.2. O benchmarkString.indexOf

Nosso primeiro benchmark usaráindexOf:

@Benchmark
public int indexOf() {
    return message.indexOf("eiusmod");
}

Vamos pesquisar em qual posição “eiusmod” está presente na variávelmessage.

6.3. O benchmarkString.contains

Nosso segundo benchmark usarácontains:

@Benchmark
public boolean contains() {
    return message.contains("eiusmod");
}

Tentaremos descobrir se o valormessagecontains “eiusmod”, o mesmosubstring usado no benchmark anterior.

6.4. O benchmarkStringUtils.containsIgnoreCase

Nosso terceiro benchmark usaráStringUtils#containsIgnoreCase:

@Benchmark
public boolean containsStringUtilsIgnoreCase() {
    return StringUtils.containsIgnoreCase(message, "eiusmod");
}

Tal como acontece com os benchmarks anteriores, vamos pesquisarsubstring no valormessage.

6.5. O benchmarkPattern

E nosso último benchmark usaráPattern:

@Benchmark
public boolean searchWithPattern() {
    return pattern.matcher(message).find();
}

Usaremos o padrão inicializado no métodosetup para criar umMatcher e poderemos chamar o métodofind, usando a mesma substring de antes.

6.6. Análise dos Resultados de Benchmarks

É importante observar quewe’re evaluating the benchmark results in nanoseconds.

Depois de executar nosso teste JMH, podemos ver o tempo médio que cada um levou:

  • contains: 14,736 ns

  • indexOf: 14.200 ns

  • containsStringUtilsIgnoreCase: 385,632 ns

  • searchWithPattern: 1014,633 ns

O métodoindexOf é o mais eficiente, seguido de perto porcontains. It makes sense that contains took longer because is using indexOf internally.

containsStringUtilsIgnoreCase demorou mais em comparação com os anteriores porque não diferencia maiúsculas de minúsculas.

searchWithPattern, levou um tempo médio ainda maior do último,proving that using Patterns is the worst alternative for this task.

7. Conclusão

Neste artigo, exploramos várias maneiras de pesquisar uma substring em aString.. Também avaliamos o desempenho das diferentes soluções.

Como sempre, o código está disponívelover on GitHub.