¿Es String.Contains () más rápido que String.IndexOf ()?

111

Tengo un búfer de cadena de aproximadamente 2000 caracteres y necesito verificar el búfer si contiene una cadena específica.
Hará la verificación en una aplicación web ASP.NET 2.0 para cada webrequest.

¿Alguien sabe si el método String.Contains funciona mejor que el 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);

Hecho de la diversión

Kb.
fuente
14
Si necesita hacer esto mil millones de veces por solicitud web, comenzaría a echar un vistazo a cosas como esta. En cualquier otro caso, no me molestaría, ya que el tiempo empleado en cualquiera de los métodos probablemente será increíblemente insignificante en comparación con la recepción de la solicitud HTTP en primer lugar.
mookid8000
2
Una de las claves para la optimización es probar en lugar de asumir, porque puede depender de muchos factores como la versión .NET, el sistema operativo, el hardware, la variación en la entrada, etc. En muchos casos, los resultados de las pruebas realizadas por otros puede ser muy diferente en su sistema.
Slai

Respuestas:

174

Containsllamadas IndexOf:

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

Que llama CompareInfo.IndexOf, que en última instancia usa una implementación CLR.

Si desea ver cómo se comparan las cadenas en CLR, esto se lo mostrará (busque CaseInsensitiveCompHelper ).

IndexOf(string)no tiene opciones y Contains()utiliza una comparación ordinal (una comparación byte a byte en lugar de intentar realizar una comparación inteligente, por ejemplo, e con é).

Por IndexOflo tanto, será un poco más rápido (en teoría), ya que IndexOfva directamente a una búsqueda de cadena usando FindNLSString de kernel32.dll (¡el poder del reflector!).

Actualizado para .NET 4.0 : IndexOf ya no usa la comparación ordinal, por lo que Contains puede ser más rápido. Vea el comentario a continuación.

Chris S
fuente
3
Esta respuesta no es ni de lejos correcta, solo eche un vistazo aquí stackoverflow.com/posts/498880/revisions para obtener la explicación
pzaj
55
Mi respuesta tiene 7 años y está basada en el marco .NET 2. La versión 4 de IndexOf()hecho usa StringComparison.CurrentCulturey Contains()usa StringComparison.Ordinalque será más rápido. Pero realmente las diferencias de velocidad de las que estamos hablando son mínimas: el punto es que uno llama al otro, y Contiene es más legible si no necesita el índice. En otras palabras, no se preocupe por eso.
Chris S
21

Probablemente, no importará en absoluto. Lea esta publicación sobre Coding Horror;): http://www.codinghorror.com/blog/archives/001218.html

Gonzalo Quero
fuente
4
Chupando al jefe, ¿estamos ...? : D Sin embargo, tiene razón, en comparación con el tiempo que se tarda en atender una solicitud http, buscar a través de una cadena corta, una vez, no es significativo.
Fowl
Una lectura muy entretenida, pero me molesta que su queja inicial con la concatenación es el uso de la memoria, luego solo prueba el tiempo dedicado a las diversas formas de combinar cadenas.
sab669
11

Contains (s2) es muchas veces (en mi computadora 10 veces) más rápido que IndexOf (s2) porque Contains usa StringComparison.Ordinal que es más rápido que la búsqueda sensible a la cultura que IndexOf hace por defecto (pero eso puede cambiar en .net 4.0 http: //davesbox.com/archive/2008/11/12/breaking-changes-to-the-string-class.aspx ).

Contiene tiene exactamente el mismo rendimiento que IndexOf (s2, StringComparison.Ordinal)> = 0 en mis pruebas, pero es más corto y deja en claro su intención.

ggf31416
fuente
2
Los cambios en .NET 4.0 aparentemente se revertieron antes de que se convirtiera en RTM, por lo que no confiaría demasiado en ese artículo blogs.msdn.com/bclteam/archive/2008/11/04/…
Stephen Kennedy
7

Estoy ejecutando un caso real (a diferencia de un punto de referencia sintético)

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

versus

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

Es una parte vital de mi sistema y se ejecuta 131,953 veces (gracias DotTrace).

Por sorprendente que sea la sorpresa , el resultado es el opuesto al esperado

  • Índice de 533ms.
  • Contiene 266ms.

: - /

net framework 4.0 (actualizado el 13-02-2012)

magallanes
fuente
1
porque INTes mucho más grande que BOOL, y IndexOf>=0causa un paso más
Eric Yin
3
Olvidaste usar ´StringComparison.Ordinal´
Davi Fiamenghi
6

Al usar Reflector, puede ver que Contains se implementa usando IndexOf. Aquí está la implementación.

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

Es probable que Contains sea un poco más lento que llamar a IndexOf directamente, pero dudo que tenga alguna importancia para el rendimiento real.

Brian Rasmussen
fuente
1
Sí, pero para usar indexof como bool, tendría que hacer la comparación fuera de la función. Eso probablemente daría el mismo resultado que Contiene, ¿no?
Gonzalo Quero
1
Probablemente, pero guarda una llamada al método (a menos que pueda estar en línea). Como dije, probablemente nunca será significativo.
Brian Rasmussen
6

Si realmente desea optimizar su código, su mejor enfoque es siempre la evaluación comparativa.

El marco .net tiene una excelente implementación de cronómetro: System.Diagnostics.Stopwatch

Andrew Harry
fuente
Es lo mejor, pero si desea un enfoque rápido, simplemente presione el botón de pausa en una sesión de depuración. Es probable que el control de código se detenga en la parte más lenta aproximadamente el 50% del tiempo .
Jeremy Thompson
4

A partir de una pequeña lectura, parece que bajo el capó el método String.Contains simplemente llama a String.IndexOf. La diferencia es que String.Contains devuelve un valor booleano, mientras que String.IndexOf devuelve un número entero con (-1) que representa que no se encontró la subcadena.

Sugeriría escribir una pequeña prueba con aproximadamente 100,000 iteraciones y verlo usted mismo. Si tuviera que adivinar, diría que IndexOf puede ser un poco más rápido, pero como dije, es solo una suposición.

Jeff Atwood tiene un buen artículo sobre cuerdas en su blog . Se trata más de concatenación, pero de todos modos puede ser útil.

Mike Roosa
fuente
3

Solo como una actualización de esto, he estado haciendo algunas pruebas y siempre que su cadena de entrada sea bastante grande, entonces Regex paralelo es el método C # más rápido que he encontrado (siempre que tenga más de un núcleo, imagino)

Obtener la cantidad total de coincidencias, por ejemplo:

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

¡Espero que esto ayude!

Gary
fuente
1
Hola, phild en un hilo separado actualizó esto con una versión de tomasp.net/articles/ahocorasick.aspx que, si sus palabras clave (agujas) no cambian, es mucho más rápido.
Gary
2

Utilice una biblioteca de referencia, como esta reciente incursión de Jon Skeet para medirla.

Caveat Emptor

Como todas las preguntas de (micro) rendimiento, esto depende de las versiones del software que esté utilizando, los detalles de los datos inspeccionados y el código que rodea la llamada.

Como todas las preguntas de (micro) rendimiento, el primer paso debe ser obtener una versión en ejecución que se pueda mantener fácilmente. Luego, la evaluación comparativa, la creación de perfiles y el ajuste se pueden aplicar a los cuellos de botella medidos en lugar de adivinar.

David Schmitt
fuente
Si bien este enlace puede responder a la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden dejar de ser válidas si cambia la página enlazada.
Mike Stockdale
la biblioteca vinculada es solo una de muchas, y no el principal impulso de la respuesta. No creo que publicar la fuente o la descripción de las bibliotecas mejoraría la respuesta, este sitio o el mundo.
David Schmitt
3
-1; la pregunta era "¿Alguien sabe si el método String.Contains funciona mejor que el método String.IndexOf?" - su respuesta es "use una biblioteca de referencia", que básicamente significa "No sé, hágalo usted mismo", "esto depende", que significa "No sé" y "obtenga una versión y un perfil en ejecución" , que también significa "No sé, hágalo usted mismo". Esto no es 'Jeopardy'; proporcione una respuesta a la pregunta formulada , no ideas de cómo hacerlo , su lugar está en los comentarios .
-7

Para cualquiera que siga leyendo esto, indexOf () probablemente funcionará mejor en la mayoría de los sistemas empresariales, ya que contains () no es compatible con IE.

Zargontapel
fuente
12
lanzar nueva OutOfScopeException ();
Raphaël