String.Contains () è più veloce di String.IndexOf ()?


111

Ho un buffer di stringa di circa 2000 caratteri e devo controllare il buffer se contiene una stringa specifica.
Farà il controllo in una webapp ASP.NET 2.0 per ogni webrequest.

Qualcuno sa se il metodo String.Contains funziona meglio del metodo 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);

Fatto divertente


14
Se hai bisogno di farlo un miliardo di volte per richiesta web, inizierei a dare un'occhiata a cose come questa. In ogni altro caso, non mi preoccuperei, dal momento che il tempo speso in entrambi i metodi sarà molto probabilmente incredibilmente insignificante rispetto alla ricezione della richiesta HTTP in primo luogo.
mookid8000

2
Una delle chiavi per l'ottimizzazione è testare invece di supporre, perché può dipendere da molti fattori come la versione .NET, il sistema operativo, l'hardware, la variazione nell'input, ecc. In molti casi i risultati dei test eseguiti da altri può essere molto diverso sul tuo sistema.
Slai

Risposte:


174

Containschiamate IndexOf:

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

Che chiama CompareInfo.IndexOf, che in ultima analisi utilizza un'implementazione CLR.

Se vuoi vedere come vengono confrontate le stringhe nel CLR, questo ti mostrerà (cerca CaseInsensitiveCompHelper ).

IndexOf(string)non ha opzioni e Contains()utilizza un confronto ordinale (un confronto byte per byte anziché tentare di eseguire un confronto intelligente, ad esempio, e con é).

Quindi IndexOfsarà leggermente più veloce (in teoria) poiché IndexOfva direttamente a una ricerca di stringhe utilizzando FindNLSString da kernel32.dll (la potenza di reflector!).

Aggiornato per .NET 4.0 : IndexOf non usa più Ordinal Comparison e quindi Contains può essere più veloce. Vedi commento sotto.


3
Questa risposta non è neanche lontanamente corretta, basta dare un'occhiata qui stackoverflow.com/posts/498880/revisions per la spiegazione
pzaj

55
La mia risposta è di 7 anni e si basa sul framework .NET 2. La versione 4 IndexOf()utilizza effettivamente StringComparison.CurrentCulturee Contains()utilizza StringComparison.Ordinalche sarà più veloce. Ma in realtà le differenze di velocità di cui stiamo parlando sono minime: il punto è che uno chiama l'altro e Contains è più leggibile se non hai bisogno dell'indice. In altre parole, non preoccuparti.
Chris S

21

Probabilmente non avrà alcuna importanza. Leggi questo post su Coding Horror;): http://www.codinghorror.com/blog/archives/001218.html


4
Succhiando il capo, siamo ...? : D Hai ragione, rispetto al tempo impiegato per servire una richiesta http, la ricerca attraverso una breve stringa, una volta, non è significativa.
Fowl

Una lettura molto divertente, ma mi dà fastidio la sua lamentela iniziale con la concatenazione è l'utilizzo della memoria, quindi verifica solo il tempo trascorso con i vari modi di combinare le stringhe.
sab669

11

Contains (s2) è molte volte (nel mio computer 10 volte) più veloce di IndexOf (s2) perché Contains utilizza StringComparison.Ordinal che è più veloce della ricerca sensibile alle impostazioni cultura che IndexOf esegue per impostazione predefinita (ma potrebbe cambiare in .net 4.0 http: //davesbox.com/archive/2008/11/12/breaking-changes-to-the-string-class.aspx ).

Contains ha esattamente le stesse prestazioni di IndexOf (s2, StringComparison.Ordinal)> = 0 nei miei test, ma è più breve e rende chiaro il tuo intento.


2
Le modifiche in .NET 4.0 sono state apparentemente ripristinate prima che diventasse
Stephen Kennedy

7

Sto conducendo un caso reale (al contrario di un benchmark sintetico)

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

contro

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

È una parte vitale del mio sistema e viene eseguito 131.953 volte (grazie DotTrace).

Per quanto sconvolgente sorpresa , il risultato è l'opposto di quanto previsto

  • Indice di 533 ms.
  • Contiene 266 ms.

: - /

net framework 4.0 (aggiornato al 13-02-2012)


1
perché INTè molto più grande di BOOL, e IndexOf>=0causa un ulteriore passo
Eric Yin

3
Hai dimenticato di usare ´StringComparison.Ordinal´
Davi Fiamenghi

6

Usando Reflector, puoi vedere che Contains è implementato usando IndexOf. Ecco l'implementazione.

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

Quindi Contains è probabilmente un po 'più lento rispetto alla chiamata diretta di IndexOf, ma dubito che avrà alcun significato per le prestazioni effettive.


1
Sì, ma per usare indexof come bool, dovrebbe fare il confronto al di fuori della funzione. Molto probabilmente darebbe lo stesso risultato di Contains, no?
Gonzalo Quero

1
Probabilmente, ma salvi una chiamata al metodo (a meno che non possa essere inline). Come ho detto, probabilmente non sarà mai significativo.
Brian Rasmussen

6

Se vuoi davvero ottimizzare al minimo il tuo codice, il tuo approccio migliore è sempre il benchmarking.

Il framework .net ha un'eccellente implementazione del cronometro: System.Diagnostics.Stopwatch


È il migliore, ma se vuoi un approccio rapido basta premere il pulsante di pausa in una sessione di debug. È probabile che il controllo del codice si interrompa nella parte più lenta circa il 50% delle volte .
Jeremy Thompson

4

Da una piccola lettura, sembra che sotto il cofano il metodo String.Contains chiama semplicemente String.IndexOf. La differenza è che String.Contains restituisce un valore booleano mentre String.IndexOf restituisce un numero intero con (-1) che rappresenta che la sottostringa non è stata trovata.

Suggerirei di scrivere un piccolo test con circa 100.000 iterazioni e vedere di persona. Se dovessi indovinare, direi che IndexOf potrebbe essere leggermente più veloce ma come ho detto è solo un'ipotesi.

Jeff Atwood ha un buon articolo sulle stringhe nel suo blog . Si tratta più di concatenazione, ma può comunque essere utile.


3

Proprio come un aggiornamento a questo ho fatto alcuni test e fornendo la tua stringa di input è abbastanza grande, quindi Regex parallelo è il metodo C # più veloce che ho trovato (a condizione che tu abbia più di un core immagino)

Ottenere la quantità totale di corrispondenze, ad esempio:

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

Spero che questo ti aiuti!


1
Ciao phild su un thread separato lo ha aggiornato con una versione da tomasp.net/articles/ahocorasick.aspx che, a condizione che le tue parole chiave (aghi) non cambino, è molto più veloce.
gary

2

Usa una libreria di benchmark, come questa recente incursione di Jon Skeet per misurarla.

Caveat Emptor

Come tutte le domande sulle (micro) prestazioni, ciò dipende dalle versioni del software in uso, dai dettagli dei dati esaminati e dal codice che circonda la chiamata.

Come tutte le domande sulle (micro) prestazioni, il primo passo deve essere quello di ottenere una versione funzionante che sia facilmente gestibile. Quindi benchmarking, profiling e tuning possono essere applicati ai colli di bottiglia misurati invece di supporre.


Sebbene questo collegamento possa rispondere alla domanda, è meglio includere le parti essenziali della risposta qui e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia.
Mike Stockdale

la libreria collegata è solo una delle tante, e non il fulcro principale della risposta. Non credo che pubblicare la fonte o la descrizione delle biblioteche migliorerebbe la risposta, questo sito o il mondo.
David Schmitt

3
-1; la domanda era "Qualcuno sa se il metodo String.Contains funziona meglio del metodo String.IndexOf?" - la tua risposta è "usa una libreria di benchmark", che in pratica significa "non lo so, fai da te", "dipende", che significa "non so" e "ottieni una versione e un profilo funzionanti" , che significa anche "Non lo so, fai da te". Questo non è "Rischio" - si prega di fornire una risposta alla domanda posta , non idee su come fare - il loro posto è nei commenti .

-7

Per chiunque stia ancora leggendo questo, indexOf () probabilmente funzionerà meglio sulla maggior parte dei sistemi aziendali, poiché contiene () non è compatibile con IE!


12
lancia una nuova OutOfScopeException ();
Raphaël
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.