String.Contains () est-il plus rapide que String.IndexOf ()?

111

J'ai un tampon de chaîne d'environ 2000 caractères et j'ai besoin de vérifier le tampon s'il contient une chaîne spécifique.
Fera la vérification dans une application Web ASP.NET 2.0 pour chaque webrequest.

Quelqu'un sait-il si la méthode String.Contains fonctionne mieux que la méthode 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);

Fait amusant

Kb.
la source
14
Si vous avez besoin de le faire un milliard de fois par requête Web, je commencerais à jeter un œil à des choses comme celle-ci. Dans tous les autres cas, je ne dérangerais pas, car le temps passé dans l'une ou l'autre méthode sera probablement incroyablement insignifiant par rapport à la réception de la requête HTTP en premier lieu.
mookid8000
2
L'une des clés de l'optimisation est de tester au lieu de supposer, car cela peut dépendre de nombreux facteurs tels que la version .NET, le système d'exploitation, le matériel, la variation de l'entrée, etc. Dans de nombreux cas, les résultats des tests sont effectués par d'autres peut être très différent sur votre système.
Slai

Réponses:

174

Containsappels IndexOf:

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

Quels appels CompareInfo.IndexOf, qui utilise finalement une implémentation CLR.

Si vous voulez voir comment les chaînes sont comparées dans le CLR, cela vous montrera (recherchez CaseInsensitiveCompHelper ).

IndexOf(string)n'a pas d'options et Contains()utilise une comparaison ordinale (une comparaison octet par octet plutôt que d'essayer d'effectuer une comparaison intelligente, par exemple, e avec é).

Ce IndexOfsera donc légèrement plus rapide (en théorie) car il IndexOfva directement à une recherche de chaîne en utilisant FindNLSString de kernel32.dll (la puissance du réflecteur!).

Mis à jour pour .NET 4.0 - IndexOf n'utilise plus la comparaison ordinale et contient donc peut être plus rapide. Voir le commentaire ci-dessous.

Chris S
la source
3
Cette réponse est loin d'être correcte, jetez un coup d'œil ici stackoverflow.com/posts/498880/revisions pour l'explication
pzaj
55
Ma réponse a 7 ans et est basée sur le framework .NET 2. La version 4 IndexOf()utilise en effet StringComparison.CurrentCultureet Contains()utilise StringComparison.Ordinalce qui sera plus rapide. Mais en réalité, les différences de vitesse dont nous parlons sont infimes - le fait est que l'un appelle l'autre, et Contains est plus lisible si vous n'avez pas besoin de l'index. En d'autres termes, ne vous en faites pas.
Chris S
21

Cela n'aura probablement pas d'importance du tout. Lisez cet article sur Coding Horror;): http://www.codinghorror.com/blog/archives/001218.html

Gonzalo Quero
la source
4
Sucer le patron, sommes-nous ...? : D Vous avez raison cependant, comparé au temps nécessaire pour traiter une requête http, rechercher une fois dans une chaîne courte n'est pas significatif.
Fowl
Une lecture très divertissante, mais cela me dérange sa plainte initiale avec la concaténation est l'utilisation de la mémoire, puis il ne teste que le temps passé avec les différentes façons de combiner des chaînes.
sab669
11

Contains (s2) est plusieurs fois (sur mon ordinateur 10 fois) plus rapide que IndexOf (s2) car Contains utilise StringComparison.Ordinal qui est plus rapide que la recherche sensible à la culture que IndexOf effectue par défaut (mais cela peut changer dans .net 4.0 http: //davesbox.com/archive/2008/11/12/breaking-changes-to-the-string-class.aspx ).

Contains a exactement les mêmes performances que IndexOf (s2, StringComparison.Ordinal)> = 0 dans mes tests, mais il est plus court et rend votre intention claire.

ggf31416
la source
2
Les modifications apportées à .NET 4.0 ont apparemment été annulées avant de passer à RTM, donc je ne me fierais
Stephen Kennedy
7

Je dirige un cas réel (à l'opposé d'un benchmark synthétique)

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

contre

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

C'est une partie vitale de mon système et elle est exécutée 131 953 fois (merci DotTrace).

Cependant choquant la surprise , le résultat est le contraire que prévu

  • IndexOf 533ms.
  • Contient 266 ms.

: - /

net framework 4.0 (mis à jour au 13-02-2012)

magallanes
la source
1
parce que INTc'est beaucoup plus grand que BOOL, et IndexOf>=0cause un pas de plus
Eric Yin
3
Vous avez oublié d'utiliser ´StringComparison.Ordinal´
Davi Fiamenghi
6

En utilisant Reflector, vous pouvez voir que Contains est implémenté à l'aide d'IndexOf. Voici la mise en œuvre.

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

So Contains est probablement un peu plus lent que d'appeler IndexOf directement, mais je doute que cela ait une importance pour les performances réelles.

Brian Rasmussen
la source
1
Oui, mais pour utiliser indexof comme booléen, il devrait faire la comparaison en dehors de la fonction. Cela donnerait probablement le même résultat que Contains, n'est-ce pas?
Gonzalo Quero le
1
Probablement, mais vous enregistrez un appel de méthode (à moins qu'il ne puisse être inséré). Comme je l'ai dit, cela ne sera probablement jamais significatif.
Brian Rasmussen
6

Si vous voulez vraiment micro-optimiser votre code, votre meilleure approche est toujours l'analyse comparative.

Le framework .net a une excellente implémentation de chronomètre - System.Diagnostics.Stopwatch

Andrew Harry
la source
C'est le meilleur, mais si vous voulez une approche rapide, appuyez simplement sur le bouton pause dans une session de débogage. Le contrôle de code est susceptible de s'arrêter dans la partie la plus lente environ 50% du temps .
Jeremy Thompson
@JeremyThompson répète la méthode "pause debug" comme 10 fois et vous vous êtes procuré un profileur
Xeuron
4

Après une petite lecture, il semble que sous le capot, la méthode String.Contains appelle simplement String.IndexOf. La différence est String.Contains renvoie un booléen tandis que String.IndexOf retourne un entier avec (-1) représentant que la sous-chaîne n'a pas été trouvée.

Je suggérerais d'écrire un petit test avec environ 100 000 itérations et de voir par vous-même. Si je devais deviner, je dirais qu'IndexOf est peut-être un peu plus rapide, mais comme je l'ai dit, il suffit de deviner.

Jeff Atwood a un bon article sur les chaînes sur son blog . Il s'agit davantage de concaténation, mais peut néanmoins être utile.

Mike Roosa
la source
3

Tout comme une mise à jour de cela, j'ai fait des tests et fourni que votre chaîne d'entrée est assez grande, alors le Regex parallèle est la méthode C # la plus rapide que j'ai trouvée (à condition que vous ayez plus d'un noyau, j'imagine)

Obtenir le nombre total de correspondances par exemple -

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

J'espère que cela t'aides!

Gary
la source
1
Salut phild sur un fil séparé mis à jour avec une version de tomasp.net/articles/ahocorasick.aspx qui, à condition que vos mots-clés (aiguilles) ne changent pas, est beaucoup plus rapide.
gary
2

Utilisez une bibliothèque de référence, comme cette récente incursion de Jon Skeet pour la mesurer.

Caveat Emptor

Comme toutes les questions de (micro-) performances, cela dépend des versions du logiciel que vous utilisez, des détails des données inspectées et du code entourant l'appel.

Comme toutes les questions de (micro-) performances, la première étape doit être d'obtenir une version en cours d'exécution qui soit facilement maintenable. Ensuite, l'analyse comparative, le profilage et le réglage peuvent être appliqués aux goulots d'étranglement mesurés au lieu de deviner.

David Schmitt
la source
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien pour référence. Les réponses aux liens uniquement peuvent devenir invalides si la page liée change.
Mike Stockdale
la bibliothèque liée n'est qu'une parmi tant d'autres, et non le principal objectif de la réponse. Je ne pense pas que publier la source ou la description des bibliothèques améliorerait la réponse, ce site ou le monde.
David Schmitt
3
-1 ; la question était "Quelqu'un sait-il si la méthode String.Contains fonctionne mieux que la méthode String.IndexOf?" - votre réponse est "utiliser une bibliothèque de référence", ce qui signifie essentiellement "je ne sais pas, faites-le vous-même", "cela dépend", ce qui signifie "je ne sais pas" et "obtenir une version et un profil en cours d'exécution" , qui signifie aussi "Je ne sais pas, fais-le toi-même". Ce n'est pas «Jeopardy» - veuillez fournir une réponse à la question posée , pas des idées de marche à suivre - leur place est dans les commentaires .
-7

Pour tous ceux qui lisent encore ceci, indexOf () fonctionnera probablement mieux sur la plupart des systèmes d'entreprise, car contains () n'est pas compatible avec IE!

Zargontapel
la source
12
throw new OutOfScopeException ();
Raphaël