String.Contains () é mais rápido que String.IndexOf ()?

111

Eu tenho um buffer de string de cerca de 2.000 caracteres e preciso verificar se ele contém uma string específica.
Fará a verificação em um webapp ASP.NET 2.0 para cada webrequest.

Alguém sabe se o método String.Contains tem melhor desempenho do que o método String.IndexOf ?

    // 2000 characters in s1, search token in s2
    string s1 = "Many characters. The quick brown fox jumps over the lazy dog"; 
    string s2 = "fox";
    bool b;
    b = s1.Contains(s2);
    int i;
    i = s1.IndexOf(s2);

Fato engraçado

Kb.
fonte
14
Se você precisar fazer isso um bilhão de vezes por solicitação da web, eu começaria a dar uma olhada em coisas como esta. Em qualquer outro caso, eu não me incomodaria, já que o tempo gasto em qualquer um dos métodos provavelmente será incrivelmente insignificante em comparação ao recebimento da solicitação HTTP em primeiro lugar.
mookid8000
2
Uma das chaves para a otimização é testar em vez de supor, porque pode depender de vários fatores, como versão do .NET, sistema operacional, hardware, variação na entrada, etc. Em muitos casos, resultados de testes feitos por outros pode ser muito diferente em seu sistema.
Slai

Respostas:

174

Containsligações IndexOf:

public bool Contains(string value)
{
    return (this.IndexOf(value, StringComparison.Ordinal) >= 0);
}

Que chama CompareInfo.IndexOf, que em última análise usa uma implementação CLR.

Se você quiser ver como as strings são comparadas no CLR, isso mostrará a você (procure por CaseInsensitiveCompHelper ).

IndexOf(string)não tem opções e Contains()usa uma comparação Ordinal (uma comparação byte a byte em vez de tentar realizar uma comparação inteligente, por exemplo, e com é).

Portanto, IndexOfserá um pouco mais rápido (em teoria), pois IndexOfvai direto para uma pesquisa de string usando FindNLSString de kernel32.dll (o poder do refletor!).

Atualizado para .NET 4.0 - IndexOf não usa mais comparação ordinal e, portanto, Contains pode ser mais rápido. Veja o comentário abaixo.

Chris S
fonte
3
Esta resposta está longe de estar correta, basta dar uma olhada aqui stackoverflow.com/posts/498880/revisions para a explicação
pzaj
55
Minha resposta tem 7 anos e é baseada no framework .NET 2. A versão 4 IndexOf()realmente usa StringComparison.CurrentCulturee Contains()usa o StringComparison.Ordinalque será mais rápido. Mas, na verdade, as diferenças de velocidade de que estamos falando são mínimas - a questão é que um chama o outro, e Contém é mais legível se você não precisar do índice. Em outras palavras, não se preocupe com isso.
Chris S de
21

Provavelmente, não fará diferença alguma. Leia esta postagem sobre Coding Horror;): http://www.codinghorror.com/blog/archives/001218.html

Gonzalo Quero
fonte
4
Sugando o chefe, estamos ...? : D Você está certo, porém, em comparação com o tempo gasto para atender a uma solicitação http, pesquisar em uma string curta, uma vez, não é significativo.
Fowl
Uma leitura muito divertida, mas me incomoda sua reclamação inicial com a concatenação é o uso de memória, então ele apenas testa o tempo gasto com as várias maneiras de combinar strings.
sab669,
11

Contains (s2) é muitas vezes (no meu computador 10 vezes) mais rápido que IndexOf (s2) porque Contains usa StringComparison.Ordinal que é mais rápido do que a pesquisa sensível à cultura que IndexOf faz por padrão (mas que pode mudar em .net 4.0 http: //davesbox.com/archive/2008/11/12/breaking-changes-to-the-string-class.aspx ).

Contains tem exatamente o mesmo desempenho que IndexOf (s2, StringComparison.Ordinal)> = 0 em meus testes, mas é mais curto e deixa sua intenção clara.

ggf31416
fonte
2
As alterações no .NET 4.0 foram aparentemente revertidas antes de ser RTM, então eu não confiaria muito naquele artigo blogs.msdn.com/bclteam/archive/2008/11/04/…
Stephen Kennedy
7

Estou executando um caso real (em oposição a um benchmark sintético)

 if("=,<=,=>,<>,<,>,!=,==,".IndexOf(tmps)>=0) {

versus

 if("=,<=,=>,<>,<,>,!=,==,".Contains(tmps)) {

É uma parte vital do meu sistema e é executado 131.953 vezes (obrigado DotTrace).

Por mais chocante surpresa , o resultado é o oposto do esperado

  • IndexOf 533ms.
  • Contém 266ms.

: - /

net framework 4.0 (atualizado em 13-02-2012)

Magalhães
fonte
1
porque INTé muito maior do que BOOLe IndexOf>=0causa mais uma etapa
Eric Yin
3
Você se esqueceu de usar o ´StringComparison.Ordinal´
Davi Fiamenghi
6

Usando o Reflector, você pode ver que o Contains é implementado usando o IndexOf. Aqui está a implementação.

public bool Contains(string value)
{
   return (this.IndexOf(value, StringComparison.Ordinal) >= 0);
}

Portanto, o Contains é provavelmente um pouco mais lento do que chamar IndexOf diretamente, mas duvido que tenha qualquer significado para o desempenho real.

Brian Rasmussen
fonte
1
Sim, mas para usar indexof como bool, ele teria que fazer a comparação fora da função. Isso provavelmente daria o mesmo resultado que Contém, não é?
Gonzalo Quero
1
Provavelmente, mas você salva uma chamada de método (a menos que possa ser embutida). Como eu disse, provavelmente nunca será significativo.
Brian Rasmussen
6

Se você realmente deseja otimizar seu código, sua melhor abordagem é sempre o benchmarking.

A estrutura .net tem uma excelente implementação de cronômetro - System.Diagnostics.Stopwatch

Andrew Harry
fonte
É o melhor, mas se você quiser uma abordagem rápida, basta pressionar o botão de pausa em uma sessão de depuração. É provável que o controle de código pare na parte mais lenta em cerca de 50% do tempo .
Jeremy Thompson
@JeremyThompson repita o método "pause debug" umas 10 vezes e você terá um perfilador
Xeuron
4

Com uma pequena leitura, parece que nos bastidores o método String.Contains simplesmente chama String.IndexOf. A diferença é String.Contains retorna um booleano enquanto String.IndexOf retorna um número inteiro com (-1) representando que a substring não foi encontrada.

Eu sugeriria escrever um pequeno teste com 100.000 ou mais iterações e ver por si mesmo. Se eu fosse adivinhar, diria que IndexOf pode ser um pouco mais rápido, mas como disse, é apenas um palpite.

Jeff Atwood tem um bom artigo sobre cordas em seu blog . É mais sobre concatenação, mas pode ser útil mesmo assim.

Mike Roosa
fonte
3

Como uma atualização para isso, estou fazendo alguns testes e fornecendo sua string de entrada é bastante grande, então Regex paralelo é o método C # mais rápido que encontrei (desde que você tenha mais de um núcleo, eu imagino)

Obtendo a quantidade total de correspondências, por exemplo -

needles.AsParallel ( ).Sum ( l => Regex.IsMatch ( haystack , Regex.Escape ( l ) ) ? 1 : 0 );

Espero que isto ajude!

Gary
fonte
1
Olá, phild, em um tópico separado atualizou isso com uma versão de tomasp.net/articles/ahocorasick.aspx que, fornecer suas palavras-chave (agulhas) não mudam é muito mais rápido.
Gary,
2

Use uma biblioteca de referência, como esta recente incursão de Jon Skeet para medi-la.

Caveat Emptor

Como todas as questões de (micro) desempenho, isso depende das versões do software que você está usando, dos detalhes dos dados inspecionados e do código que envolve a chamada.

Como todas as questões de (micro) desempenho, o primeiro passo deve ser obter uma versão em execução que seja de fácil manutenção. Em seguida, benchmarking, criação de perfil e ajuste podem ser aplicados aos gargalos medidos em vez de adivinhação.

David Schmitt
fonte
Embora este link possa responder à pergunta, é melhor incluir as partes essenciais da resposta aqui e fornecer o link para referência. As respostas somente com link podem se tornar inválidas se a página vinculada mudar.
Mike Stockdale
a biblioteca vinculada é apenas uma entre muitas, e não o principal objetivo da resposta. Não acho que postar a fonte ou descrição das bibliotecas melhoraria a resposta, este site ou o mundo.
David Schmitt
3
-1; a pergunta era "Alguém sabe se o método String.Contains tem melhor desempenho do que o método String.IndexOf?" - sua resposta é "usar uma biblioteca de referência", que basicamente significa "Não sei, faça você mesmo", "isso depende", que significa "Não sei" e "obtenha uma versão e perfil em execução" , que também significa "Não sei, faça você mesmo". Isso não é 'Jeopardy' - forneça uma resposta à pergunta feita , não ideias de como fazer - o lugar delas é nos comentários .
-7

Para quem ainda está lendo isso, indexOf () provavelmente terá um desempenho melhor na maioria dos sistemas corporativos, pois contains () não é compatível com o IE!

Zargontapel
fonte
12
lance novo OutOfScopeException ();
Raphaël