¿Cómo funcionan el StartsWith, Contains o EndsWith?

En .Net tenemos una función llamada StartsWith para saber si un String comienza con una serie de caracteres determinados, en principio, yo me imagino que si dicha función determina que el primer carácter ya no coincide dejará de comparar el resto y devolverá un false, no obstante he querido comprobar si eso es cierto comparando cuánto tarda con un String con el que si empieza, con otro con el que no empieza o con uno con el que coincide a medias; y para que no haya dudas, un poco a lo bestia… metiéndolo en un bucle de 5 millones de pasadas.

Bucle Segundos
StartsWith No Coincidente 1,789597
StartsWith Casi Coincidente 2,186570
StartsWith Coincidente 0,081005

Me sorprende que el coincidente sea el que menos consuma, ¿significa eso que .Net sigue comparando el resto de caracteres? Pero lo que me deja sin explicación alguna es ver que el coincidente a medias es el que más tiempo tarda… podría pensar que este string es más largo que el anterior y tendría sentido, pero sería absurdo pensar que el algoritmo StartsWith sigue comparando caracteres aunque ya haya encontrado uno no coincidente… ¿No les enseñaron a usar el Exit While?

Y ya que estoy, lo comparo con el Contains y…

Bucle Segundos
Contains No Coincidente 1,015006
Contains Casi Coincidente 1,025467
Contains Coincidente 1,116619

Parece que el Contains está bastante mejor optimizado, parece un absurdo ya que el StartsWith lo tiene más fácil, pero los resultados no engañan. Las diferencias en este caso son menores pero se aprecia que el No Coincidente tarda menos que el Coincidente a Medias, que a su vez tarda menos que el Coincidente, lo que daría sentido a la teoría de que deja de comparar chars en cuanto uno rompe la equivalencia. El StarsWith coincidente sí consume menos, pero usarlo conlleva el riesgo de topar con un no coincidente y tardar más.

¿Y el EndsWith? ¿Funcionará igual?

Bucle Segundos
EndsWith No Coincidente 3,723053
EndsWith Casi Coincidente 3,722809
EndsWith Coincidente 0,081173

Bueno, lo del EndsWith ya resulta hasta triste, no sólo es el que más tarda si no que además vuelve a demostrar que no parece que hayan sabido hacer un algoritmo con lógica. Al ser un EndsWith tendrían que comparar los strings a la inversa, desde atrás hacia adelante, y saliendo del bucle en cuanto encontrase el primer char no coincidente, pero por los resultados obtenidos mas parece que se ponga a buscar a Wally y a tomarse una cerveza por el camino… ahora sólo faltaría hacer lo mismo en C# para ver si funciona mejor, de hacerlo, el mito del C# resultaría cierto… pero creo que no me atrevo a desmitificar mi querido Visual Basic. Oh Microsoft, ¿por qué nos has abandonado?

Aquí va el código por si queréis verlo por vosotros mismos o podéis encontrar en dónde me he equivocado para obtener estos incomprensibles resultados (que dado que ya me equivoqué con el experimento del otro día, no sería nada raro):

  Private Sub PruebaRendimiento_StartsWith()
        Dim i As Double
        Dim META As Double = 5000000

        Dim strPalabra As String = "escalifragilisticoespialidoso"
        Dim strCoincide As String = "escalifragilisticoespialidoso"
        Dim strCoincidenteAMedias As String = "escalifragilistPosVaSerQueNo"
        Dim strNoCoincide As String = "PosVaSerQueNo"

        Me.Trace.IsEnabled = True
        Me.Trace.Warn("PRUEBA DE RENDIMIENTO. START:")


        '**************************
        'STARTSWITH:

        'No
        Me.Trace.Warn("Empiezo bucle StartsWith no coincidente.")
        For i = 0 To META
            If strPalabra.StartsWith(strNoCoincide) Then
            End If
        Next
        Me.Trace.Warn("Bucle StartsWith no coincidente terminado.____________________________")

        'Casi
        Me.Trace.Warn("Empiezo bucle StartsWith casi coincidente.")
        For i = 0 To META
            If strPalabra.StartsWith(strCoincidenteAMedias) Then
            End If
        Next
        Me.Trace.Warn("Bucle StartsWith casi coincidente terminado.____________________________")

        'Si
        Me.Trace.Warn("Empiezo bucle StartsWith coincidente.")
        For i = 0 To META
            If strPalabra.StartsWith(strCoincide) Then
            End If
        Next
        Me.Trace.Warn("Bucle StartsWith coincidente terminado.____________________________")


        '**************************
        'CONTAINS:

        'No
        Me.Trace.Warn("Empiezo bucle Contains no coincidente.")
        For i = 0 To META
            If strPalabra.Contains(strNoCoincide) Then
            End If
        Next
        Me.Trace.Warn("Bucle Contains no coincidente terminado.____________________________")

        'Casi
        Me.Trace.Warn("Empiezo bucle Contains casi coincidente.")
        For i = 0 To META
            If strPalabra.Contains(strCoincidenteAMedias) Then
            End If
        Next
        Me.Trace.Warn("Bucle Contains casi coincidente terminado.____________________________")

        'Si
        Me.Trace.Warn("Empiezo bucle Contains coincidente.")
        For i = 0 To META
            If strPalabra.Contains(strCoincide) Then
            End If
        Next
        Me.Trace.Warn("Bucle Contains coincidente terminado.____________________________")


        '**************************
        'ENDSWITH:
        strCoincidenteAMedias = "PosNoticoespialidoso"

        'No
        Me.Trace.Warn("Empiezo bucle EndsWith no coincidente.")
        For i = 0 To META
            If strPalabra.EndsWith(strNoCoincide) Then
            End If
        Next
        Me.Trace.Warn("Bucle EndsWith no coincidente terminado.____________________________")

        'Casi
        Me.Trace.Warn("Empiezo bucle EndsWith casi coincidente.")
        For i = 0 To META
            If strPalabra.EndsWith(strCoincidenteAMedias) Then
            End If
        Next
        Me.Trace.Warn("Bucle EndsWith casi coincidente terminado.____________________________")

        'Si
        Me.Trace.Warn("Empiezo bucle EndsWith coincidente.")
        For i = 0 To META
            If strPalabra.EndsWith(strCoincide) Then
            End If
        Next
        Me.Trace.Warn("Bucle EndsWith coincidente terminado.____________________________")

    End Sub

2 thoughts on “¿Cómo funcionan el StartsWith, Contains o EndsWith?”

  1. Declaraste las strings como literals entonces .Net almanece “escalifragilisticoespialidoso” y strPalabra es una referencia a esta string. Al llegar a la declaracion de strCoincide encuentra que ya tiene el “escalifragilisticoespialidoso” entonces le da a strCoincide al misma referencia. La comparacion en el caso coincidente entonces se limite a compara las referencias. Esa posibilidad se basa en la inmutabilidad de String.

  2. Wow, Michael, impresionante.

    Estoy por ponerme a colgar en el Blog todos los experimentos que haga, porque si en cada uno de ellos me corrigen y aprendo tantas cosas vale mucho la pena.

    No tenía ni idea de eso que me hablas, interesantísimo, si tengo tiempo intentaré probar el algoritmo de una forma distinta para que no pase eso a ver que tal 😉

Comments are closed.