2009-09-20 19 views
29

¿Cuál es el método de comparación incorporado más rápido para los tipos de cadena en C#? No me importa el significado tipográfico/semántico: el objetivo es usar el comparador en listas ordenadas para buscar rápidamente en grandes colecciones. Creo que solo hay dos métodos: Compare y CompareOrdinal. ¿Cuál es el más rápido?¿Cuál es la comparación más rápida (incorporada) para los tipos de cadena en C#

Además, ¿hay un método más rápido para esas comparaciones de cadenas?

Respuesta

53

Supongo que quiere una comparación menor que/igual/mayor que la igualdad; la igualdad es un tema ligeramente diferente, aunque los principios son básicamente los mismos. Si en realidad solo está buscando presencia en algo así como SortedList, consideraría usar un Dictionary<string, XXX> en su lugar - ¿realmente necesita toda esa clasificación?

String.CompareOrdinal, o usando una sobrecarga de String.Compare que permite proporcionar la comparación, y especificando una comparación ordinal (sensible a mayúsculas y minúsculas), p. String.Compare(x, y, StringComparison.Ordinal) será el más rápido.

Básicamente una comparación ordinal solo necesita recorrer las dos cadenas, carácter por carácter, hasta que encuentre la diferencia. Si no encuentra ninguna diferencia, y las longitudes son las mismas, el resultado es 0. Si no encuentra ninguna diferencia, pero las longitudes no son las mismas, la cadena más larga se considera "más grande". Si hace encuentra una diferencia, de inmediato se puede calcular que se considera "más grande" según el carácter que sea "más grande" en términos ordinales.

Poner es de otra manera: es como hacer la comparación obvia entre dos valores de char[].

Las comparaciones sensibles a la cultura tienen que realizar todo tipo de hazañas tortuosas, dependiendo de la cultura precisa que utilice. Para ver un ejemplo de esto, vea this question. Está bastante claro que tener reglas más complejas a seguir puede hacer que esto sea más lento.

+0

¿Hay una diferencia entre 'String.Compare (x, y, StringCompare.Ordinal)' y 'String.CompareOrdinal (x, y)'? ¿O uno de esos simplemente llama al otro? – Nick

+0

@Nick: esperaría que uno llamara al otro, pero no sé en qué dirección. –

4

Lo más rápido es internar cadenas con la prueba de igualdad de referencia, pero solo obtiene la prueba de igualdad y es un gran gasto de memoria, tan caro que casi nunca es el curso recomendado.

Pasado eso, una prueba ordinal sensible a mayúsculas y minúsculas será la más rápida, y este método es absolutamente recomendable para cadenas no específicas de la cultura. Las mayúsculas y minúsculas son más rápidas si funcionan para su caso de uso.

Cuando especifica ya sea StringComparison.Ordinal o StringComparison.OrdinalIgnoreCase, la comparación de cadenas no será lingüística. Es decir, las funciones que son específicas del lenguaje natural se ignoran al tomar decisiones de comparación. Esto significa que las decisiones se basan en comparaciones de bytes simples e ignoran las tablas de casing o equivalencias que se parametrizan por cultura. Como resultado, al establecer explícitamente el parámetro en StringComparison.Ordinal o StringComparison.OrdinalIgnoreCase, su código a menudo gana velocidad, aumenta la corrección y se vuelve más confiable.

Source

+1

Un ejemplo de comparación lingüística sería encyclopædia == enciclopedia, que son iguales en algunas culturas. Fuente: https://msdn.microsoft.com/en-us/library/system.stringcomparison.aspx – arni

1

he comprobado tanto la string.Compare y cadena.CompareOrdinal usando cronómetro

--Compare Ordinal case 1 
    Stopwatch sw = new Stopwatch(); 
    sw.Start(); 
    int x = string.CompareOrdinal("Jaswant Agarwal", "Jaswant Agarwal"); 
    sw.Stop(); 
    lblTimeGap.Text = sw.Elapsed.ToString(); 






    -- Only compare case 2 
    Stopwatch sw = new Stopwatch(); 
    sw.Start(); 
    int x = string.Compare("Jaswant Agarwal", "Jaswant Agarwal"); 
    sw.Stop(); 
    lblTimeGap.Text = sw.Elapsed.ToString(); 

En el caso 1 de temporización transcurrido promedio fue de 00: 00: 00,0000030 En el caso 2 de temporización transcurrido promedio fue de 00: 00: 00,0000086

I trataron con diferentes combinaciones iguales y no iguales de cuerda y encontró que cada vez es más rápido que CompareOrdinal única comparar ..

esa es mi propia observation..you también se puede tratar sólo hay que poner dos botones en un formulario y copiar y pegar este código en la reclasificación evento ..

+5

No es una comparación válida, porque el tamaño de muestra es demasiado pequeño y no ha ignorado el tiempo que tarda para JET el código. Una mejor comparación sería 1) construir como versión 2) hacer la comparación 10,000 veces, 3) comparar el promedio de los dos métodos. De esta forma, se reducirá al mínimo el ruido asociado con los efectos JIT y las cosas de fondo que su computadora está haciendo. – rianjs

+0

Totalmente de acuerdo con @rianjs, ¿cómo puedes hacerlo una vez y decir que uno es más rápido que el otro cuando se trata de millonésimas de diferencia? – nashwan

3

Diseñé una prueba unitaria para probar la velocidad de comparación de la cuerda usando algunos de los métodos mencionados en esta publicación. Esta prueba se ejecutó usando .NET 4

En resumen, no hay mucha diferencia, y tuve que ir a 100.000,000 iteraciones para ver una diferencia significativa. Dado que parece que los personajes se comparan sucesivamente hasta que se encuentra una diferencia, inevitablemente, la similitud de las cuerdas juega un papel.

Estos resultados en realidad parecen sugerir que el uso de str1.Equals (str2) es la forma más rápida de comparar cadenas.

Estos son los resultados de la prueba, con la clase de prueba incluye:

######## SET 1 compared strings are the same: 0 
#### Basic == compare: 413 
#### Equals compare: 355 
#### Equals(compare2, StringComparison.Ordinal) compare: 387 
#### String.Compare(compare1, compare2, StringComparison.Ordinal) compare: 426 
#### String.CompareOrdinal(compare1, compare2) compare: 412 

######## SET 2 compared strings are NOT the same: 0 
#### Basic == compare: 710 
#### Equals compare: 733 
#### Equals(compare2, StringComparison.Ordinal) compare: 840 
#### String.Compare(compare1, compare2, StringComparison.Ordinal) compare: 987 
#### String.CompareOrdinal(compare1, compare2) compare: 776 

using System; 
using System.Diagnostics; 
using NUnit.Framework; 

namespace Fwr.UnitTests 
{ 
    [TestFixture] 
    public class StringTests 
    { 
     [Test] 
     public void Test_fast_string_compare() 
     { 
      int iterations = 100000000; 
      bool result = false; 
      var stopWatch = new Stopwatch(); 

      Debug.WriteLine("######## SET 1 compared strings are the same: " + stopWatch.ElapsedMilliseconds); 

      string compare1 = "xxxxxxxxxxxxxxxxxx"; 
      string 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(); 
     } 
    } 
} 
+0

Esto es interesante, ya que la comparación Ordinal se usa por defecto. Al observar la fuente del marco, el tiempo extra parece atribuirse a la validación del argumento. Por lo tanto, para el rendimiento, no debe especificar StringComparison.Ordinal explícitamente, solo use la sobrecarga predeterminada. – arni

11

acabo de notar un aumento de rendimiento del 50% en mi propio código mediante la comparación de longitudes de cadena primero y si es igual a continuación, utilizando la cuerda .compare métodos. Así que en un bucle que tengo:

VB:

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

C#:

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

Esto podría depender de sus propias cadenas, pero su parece haber funcionado bien para mí .

+0

¡Noté casi el 60% con esto! Muchas gracias. –

+0

Como C# usa lógica booleana de cortocircuito, no necesita sentencias 'if' anidadas -' bool isEqual = strA.Length == strB.Length && string.Compare (strA, strB, true) == 0; ' . VB.NET también admite la lógica booleana de cortocircuito utilizando los operadores 'AndAlso' y' OrElse' - 'Dim isEquals = strA.Length = strB.Length AndAlso String.Compare (strA, strB, true) = 0'. –

1

Esto podría ser útil para alguien, pero cambiar una línea de mi código redujo la unidad de prueba de mi método de 140 ms a 1 ms.

original prueba

Unidad: 140ms

public bool StringsMatch(string string1, string string2) 
{ 
    if (string1 == null && string2 == null) return true; 
    return string1.Equals(string2, StringComparison.Ordinal); 
} 

Nueva

prueba Unidad: 1 ms

public bool StringsMatch(string string1, string string2) 
{ 
    if (string1 == null && string2 == null) return true; 
    return string.CompareOrdinal(string1, string2) == 0 ? true : false; 
} 

Unidad Prueba (NUnit)

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

Curiosamente StringsMatch_OnlyString1NullOrEmpty_ReturnFalse() era la única prueba de la unidad que se llevó a 140ms para el método StringsMatch. StringsMatch_AllParamsNullOrEmpty_ReturnTrue() siempre fue 1ms y StringsMatch_OnlyString2NullOrEmpty_ReturnFalse() siempre < 1ms.

+2

Ejecutar una prueba una vez no es una forma adecuada de medir el rendimiento. Los 140 ms declarados fueron probablemente el tiempo adicional para manejar la NullReferenceException lanzada. – arni

1

Esta es una pregunta bastante antigua, pero desde que la encontré, otras también podrían hacerlo.

Al investigar este tema un poco más, me encontré con un interesting blog post que compara todos los métodos para la comparación de cadenas. Probablemente no sea muy científico, pero sigue siendo un buen número de habitantes.

Gracias a este artículo comencé a usar string.CompareOrdinal en un escenario en el que tenía que averiguar si una cadena estaba en una lista de 170.000 cadenas y hacer esto 1600 veces seguidas. string.CompareOrdinal lo hizo casi un 50% más rápido en comparación con string.Equals

Cuestiones relacionadas