Quelle est la comparaison la plus rapide (intégrée) pour les types de chaînes en C #

Quelle est la méthode de comparaison intégrée la plus rapide pour les types de chaînes en C #? La signification typographique / sémantique ne me dérange pas: l’objective est d’utiliser le comparateur dans des listes sortingées afin d’effectuer une recherche rapide dans les grandes collections. Je pense qu’il n’y a que deux méthodes: Compare et CompareOrdinal . Quel est le plus rapide?

De plus, existe-t-il une méthode plus rapide pour ces comparaisons de chaînes?

Je suppose que vous voulez une comparaison inférieure à / égale / supérieure à plutôt qu’une simple égalité; l’égalité est un sujet légèrement différent, bien que les principes soient fondamentalement les mêmes. Si vous recherchez uniquement une présence dans quelque chose comme une liste SortedList , utilisez plutôt un Dictionary – avez-vous vraiment besoin de tout ce sorting?

Ssortingng.CompareOrdinal , ou en utilisant une surcharge de Ssortingng.Compare qui permet de fournir la comparaison, et en spécifiant une comparaison ordinale (sensible à la casse), par exemple Ssortingng.Compare(x, y, SsortingngComparison.Ordinal) sera le plus rapide.

Fondamentalement, une comparaison ordinale a juste besoin de marcher sur les deux chaînes, caractère par caractère, jusqu’à ce qu’elle trouve une différence. S’il ne trouve aucune différence et que les longueurs sont identiques, le résultat est 0. S’il ne détecte aucune différence mais que les longueurs ne sont pas identiques, la chaîne la plus longue est considérée comme “plus grande”. S’il trouve une différence, il peut immédiatement déterminer ce qui est considéré comme “plus grand” en fonction du caractère “plus grand” en termes ordinaux.

En d’autres termes, c’est comme faire la comparaison évidente entre deux valeurs char[] .

Les comparaisons tenant compte de la culture doivent exécuter toutes sortes de prouesses tortueuses, en fonction de la culture précise que vous utilisez. Pour un exemple, voir cette question . Il est clair que le fait de suivre des règles plus complexes peut ralentir le processus.

Je viens de remarquer une augmentation de 50% des performances de mon propre code en comparant d’abord les longueurs de chaîne et, si elles sont égales par la suite, à l’aide des méthodes ssortingng.compare. Donc dans une boucle, j’ai:

VB:

 If strA.length = strB.length then if ssortingng.compare(strA,strB,true) = 0 then TheyAreEqual End if End if 

C #:

 if(strA.Length == strB.Length) { if(ssortingng.Compare(strA,strB,true) == 0) { //they are equal } } 

Cela pourrait dépendre de vos propres chaînes mais cela semble avoir bien fonctionné pour moi.

Le plus rapide est celui des chaînes internées avec test d’égalité de référence, mais vous n’obtenez que des tests d’égalité et c’est une dépense importante en mémoire – si chère que ce n’est presque jamais le cours recommandé .

Après cela, un test ordinal sensible à la casse sera le plus rapide, et cette méthode est absolument recommandée pour les chaînes non spécifiques à une culture. Le respect de la casse est plus rapide si cela fonctionne pour votre cas d’utilisation.

Lorsque vous spécifiez SsortingngComparison.Ordinal ou SsortingngComparison.OrdinalIgnoreCase , la comparaison de chaînes sera non linguistique. C’est-à-dire que les fonctionnalités spécifiques au langage naturel sont ignorées lors de la prise de décisions de comparaison. Cela signifie que les décisions sont basées sur de simples comparaisons d’octets et ignorent les tableaux de casse ou d’équivalence paramétrés par la culture. Par conséquent, en définissant explicitement le paramètre sur SsortingngComparison.Ordinal ou SsortingngComparison.OrdinalIgnoreCase , votre code gagne souvent en rapidité , augmente la SsortingngComparison.Ordinal et devient plus fiable.

La source

J’ai conçu un test unitaire pour tester la vitesse de comparaison des chaînes en utilisant certaines des méthodes mentionnées dans ce post. Ce test a été exécuté avec .NET 4

En bref, il n’y a pas beaucoup de différence, et j’ai dû aller à 100 000 000 itérations pour voir une différence significative. Comme il semble que les personnages sont comparés à tour de rôle jusqu’à ce qu’une différence soit trouvée, la similitude des chaînes joue inévitablement un rôle.

Ces résultats semblent en fait suggérer que l’utilisation de str1.Equals (str2) est le moyen le plus rapide de comparer des chaînes.

Ce sont les résultats du test, avec la classe de test incluse:

 ######## SET 1 compared ssortingngs are the same: 0 #### Basic == compare: 413 #### Equals compare: 355 #### Equals(compare2, SsortingngComparison.Ordinal) compare: 387 #### Ssortingng.Compare(compare1, compare2, SsortingngComparison.Ordinal) compare: 426 #### Ssortingng.CompareOrdinal(compare1, compare2) compare: 412 ######## SET 2 compared ssortingngs are NOT the same: 0 #### Basic == compare: 710 #### Equals compare: 733 #### Equals(compare2, SsortingngComparison.Ordinal) compare: 840 #### Ssortingng.Compare(compare1, compare2, SsortingngComparison.Ordinal) compare: 987 #### Ssortingng.CompareOrdinal(compare1, compare2) compare: 776 using System; using System.Diagnostics; using NUnit.Framework; namespace Fwr.UnitTests { [TestFixture] public class SsortingngTests { [Test] public void Test_fast_ssortingng_compare() { int iterations = 100000000; bool result = false; var stopWatch = new Stopwatch(); Debug.WriteLine("######## SET 1 compared ssortingngs are the same: " + stopWatch.ElapsedMilliseconds); ssortingng compare1 = "xxxxxxxxxxxxxxxxxx"; ssortingng compare2 = "xxxxxxxxxxxxxxxxxx"; // Test 1 stopWatch.Start(); for (int i = 0; i < iterations; i++) { result = compare1 == compare2; } stopWatch.Stop(); Debug.WriteLine("#### Basic == compare: " + stopWatch.ElapsedMilliseconds); stopWatch.Reset(); // Test 2 stopWatch.Start(); for (int i = 0; i < iterations; i++) { result = compare1.Equals(compare2); } stopWatch.Stop(); Debug.WriteLine("#### Equals compare: " + stopWatch.ElapsedMilliseconds); stopWatch.Reset(); // Test 3 stopWatch.Start(); for (int i = 0; i < iterations; i++) { result = compare1.Equals(compare2, StringComparison.Ordinal); } stopWatch.Stop(); Debug.WriteLine("#### Equals(compare2, StringComparison.Ordinal) compare: " + stopWatch.ElapsedMilliseconds); stopWatch.Reset(); // Test 4 stopWatch.Start(); for (int i = 0; i < iterations; i++) { result = String.Compare(compare1, compare2, StringComparison.Ordinal) != 0; } stopWatch.Stop(); Debug.WriteLine("#### String.Compare(compare1, compare2, StringComparison.Ordinal) compare: " + stopWatch.ElapsedMilliseconds); stopWatch.Reset(); // Test 5 stopWatch.Start(); for (int i = 0; i < iterations; i++) { result = String.CompareOrdinal(compare1, compare2) != 0; } stopWatch.Stop(); Debug.WriteLine("#### String.CompareOrdinal(compare1, compare2) compare: " + stopWatch.ElapsedMilliseconds); stopWatch.Reset(); Debug.WriteLine("######## SET 2 compared strings are NOT the same: " + stopWatch.ElapsedMilliseconds); compare1 = "ueoqwwnsdlkskjsowy"; compare2 = "sakjdjsjahsdhsjdak"; // Test 1 stopWatch.Start(); for (int i = 0; i < iterations; i++) { result = compare1 == compare2; } stopWatch.Stop(); Debug.WriteLine("#### Basic == compare: " + stopWatch.ElapsedMilliseconds); stopWatch.Reset(); // Test 2 stopWatch.Start(); for (int i = 0; i < iterations; i++) { result = compare1.Equals(compare2); } stopWatch.Stop(); Debug.WriteLine("#### Equals compare: " + stopWatch.ElapsedMilliseconds); stopWatch.Reset(); // Test 3 stopWatch.Start(); for (int i = 0; i < iterations; i++) { result = compare1.Equals(compare2, StringComparison.Ordinal); } stopWatch.Stop(); Debug.WriteLine("#### Equals(compare2, StringComparison.Ordinal) compare: " + stopWatch.ElapsedMilliseconds); stopWatch.Reset(); // Test 4 stopWatch.Start(); for (int i = 0; i < iterations; i++) { result = String.Compare(compare1, compare2, StringComparison.Ordinal) != 0; } stopWatch.Stop(); Debug.WriteLine("#### String.Compare(compare1, compare2, StringComparison.Ordinal) compare: " + stopWatch.ElapsedMilliseconds); stopWatch.Reset(); // Test 5 stopWatch.Start(); for (int i = 0; i < iterations; i++) { result = String.CompareOrdinal(compare1, compare2) != 0; } stopWatch.Stop(); Debug.WriteLine("#### String.CompareOrdinal(compare1, compare2) compare: " + stopWatch.ElapsedMilliseconds); stopWatch.Reset(); } } } 

C’est une question assez ancienne, mais depuis que je l’ai trouvée, d’autres pourraient en faire de même.

En approfondissant un peu la recherche sur ce sujet, je suis tombé sur un article de blog intéressant qui compare toutes les méthodes de comparaison de chaînes. Probablement pas très scientifique mais toujours un bon numéro de maison.

Grâce à cet article, j’ai commencé à utiliser ssortingng.CompareOrdinal dans un scénario dans lequel je devais déterminer si une chaîne figurait dans une liste de 170 000 autres chaînes et le faisait 1 600 fois de suite. ssortingng.CompareOrdinal l’a rendu presque 50% plus rapide que ssortingng.Equals

J’ai vérifié le ssortingng.Compare et le ssortingng.CompareOrdinal à l’aide du chronomètre

  --Compare Ordinal case 1 Stopwatch sw = new Stopwatch(); sw.Start(); int x = ssortingng.CompareOrdinal("Jaswant Agarwal", "Jaswant Agarwal"); sw.Stop(); lblTimeGap.Text = sw.Elapsed.ToSsortingng(); -- Only compare case 2 Stopwatch sw = new Stopwatch(); sw.Start(); int x = ssortingng.Compare("Jaswant Agarwal", "Jaswant Agarwal"); sw.Stop(); lblTimeGap.Text = sw.Elapsed.ToSsortingng(); 

Dans le cas 1, le temps écoulé moyen était de 00: 00: 00.0000030. Dans le cas 2, le temps écoulé moyen était de 00: 00: 00.0000086.

J’ai essayé avec différentes combinaisons de chaînes égales et non égales et j’ai constaté que CompareOrdinal est chaque fois plus rapide que comparer.

C’est ma propre observation .. vous pouvez également essayer de simplement mettre deux boutons sur un formulaire et de copier / coller ce code lors d’un événement de reclassement.

Cela pourrait être utile à quelqu’un, mais modifier une ligne de mon code a ramené le test unitaire de ma méthode de 140 ms à 1 ms!

Original

Test unitaire: 140ms

 public bool SsortingngsMatch(ssortingng ssortingng1, ssortingng ssortingng2) { if (ssortingng1 == null && ssortingng2 == null) return true; return ssortingng1.Equals(ssortingng2, SsortingngComparison.Ordinal); } 

Nouveau

Test unitaire: 1ms

 public bool SsortingngsMatch(ssortingng ssortingng1, ssortingng ssortingng2) { if (ssortingng1 == null && ssortingng2 == null) return true; return ssortingng.CompareOrdinal(ssortingng1, ssortingng2) == 0 ? true : false; } 

Test unitaire (NUnit)

 [Test] public void SsortingngsMatch_OnlySsortingng1NullOrEmpty_ReturnFalse() { Authentication auth = new Authentication(); Assert.IsFalse(auth.SsortingngsMatch(null, "foo")); Assert.IsFalse(auth.SsortingngsMatch("", "foo")); } 

Fait intéressant, SsortingngsMatch_OnlySsortingng1NullOrEmpty_ReturnFalse () était le seul test unitaire qui prenait 140 ms pour la méthode SsortingngsMatch. SsortingngsMatch_AllParamsNullOrEmpty_ReturnTrue () a toujours 1 ms et SsortingngsMatch_OnlySsortingng2NullOrEmpty_ReturnFalse () toujours <1 ms.

Je pense que la plupart des développeurs C # comparent les chaînes de plusieurs manières, les suivantes étant les plus courantes:

  • Compare – comme vous l’avez mentionné
  • CompareOrdinal – comme vous l’avez mentionné
  • ==
  • Ssortingng.Equals
  • écrire un algorithme personnalisé pour comparer caractère par caractère

Si vous voulez aller à l’extrême, vous pouvez utiliser d’autres objects / méthodes moins évidents:

  • Exemple SequenceEqual :

    c1 = str1.ToCharArray(); c2 = str2.ToCharArray(); if (c1.SequenceEqual(c2))

  • Exemple d’ if (ssortingngsWeAreComparingAgainst.IndexOf(ssortingngsWeWantToSeeIfMatches, 0 , ssortingngsWeWantToSeeIfMatches.Length) == 0) : if (ssortingngsWeAreComparingAgainst.IndexOf(ssortingngsWeWantToSeeIfMatches, 0 , ssortingngsWeWantToSeeIfMatches.Length) == 0)

  • Ou vous pouvez implémenter Dictionary et HashSets, en utilisant les chaînes comme “clés” et en testant si elles existent déjà avec la chaîne avec laquelle vous souhaitez comparer. Par exemple: if (hs.Contains(ssortingngsWeWantToSeeIfMatches))

Alors n’hésitez pas à trancher et à dés pour trouver vos propres façons de faire les choses. N’oubliez pas que quelqu’un va devoir gérer le code et ne voudra probablement pas perdre de temps à essayer de comprendre pourquoi vous utilisez la méthode que vous avez choisie.

Comme toujours, optimisez à vos risques et périls. 🙂