La forma más rápida de eliminar el primer carácter en una cadena

207

Digamos que tenemos la siguiente cadena

string data= "/temp string";

Si queremos eliminar el primer personaje /, podemos hacerlo de muchas maneras, tales como:

data.Remove(0,1);
data.TrimStart('/');
data.Substring(1);

Pero, realmente no sé cuál tiene el mejor algoritmo y lo hago más rápido.
¿Hay uno que sea el mejor o todos sean iguales?

Amr Badawy
fuente
¿Desea eliminar el primer carácter de todos modos o necesita verificar que este personaje es realmente un /?
SRKX
55
TrimStartno eliminará el primer carácter, eliminará los ncaracteres desde el principio. SubstringEs el más rápido.
Jaroslav Jandek
solo necesito eliminar cualquier primer personaje
Amr Badawy
66
Si está eliminando cualquier primer personaje, TrimStart()está completamente fuera de discusión.
BoltClock
@BoltClock: sí, eso es lo que dije (escrito).
Jaroslav Jandek

Respuestas:

147

La segunda opción realmente no es la misma que las otras: si la cadena es "/// foo" se convertirá en "foo" en lugar de "// foo".

La primera opción necesita un poco más de trabajo para comprender que la tercera: vería la Substringopción como la más común y legible.

(Obviamente, cada uno de ellos como una declaración individual no hará nada útil; deberá asignar el resultado a una variable, posiblemente a datasí mismo).

No tomaría en cuenta el rendimiento aquí a menos que en realidad se convirtiera en un problema para usted, en cuyo caso la única forma en que lo sabría sería tener casos de prueba, y luego es fácil ejecutar esos casos de prueba para cada opción y compara los resultados. Esperaría Substringser probablemente el más rápido aquí, simplemente porque Substringsiempre termina creando una cadena a partir de un solo fragmento de la entrada original, mientras que Removetiene que al menos potencialmente unir un fragmento de inicio y un fragmento de final.

Jon Skeet
fuente
36
Verifico ahora llamando a cada uno sobre 90000000 y obtengo el siguiente resultado: Eliminar: 06.63 - TrimStart: 04.71 - subString: 03.09, por lo que la subcadena de resultados es la mejor
Amr Badawy
55
Solo recuerde que cuando prueba el rendimiento de esta manera, se ve afectado por el almacenamiento en caché de la CPU, por lo que debe hacerlo en las cadenas aleatorias, que rellenó previamente una matriz (lista) y seleccionó al azar el elemento de esa matriz ( lista).
ajeh
12

Sé que esta es una tierra de hiperoptimización, pero me pareció una buena excusa BenchmarkDotNet. El resultado de esta prueba (incluso en .NET Core) es que Substringes ligeramente más rápido que Remove, en esta prueba de muestra: 19.37ns vs 22.52ns paraRemove . Entonces, ~ 16% más rápido.

using System;
using BenchmarkDotNet.Attributes;

namespace BenchmarkFun
{
    public class StringSubstringVsRemove
    {
        public readonly string SampleString = " My name is Daffy Duck.";

        [Benchmark]
        public string StringSubstring() => SampleString.Substring(1);

        [Benchmark]
        public string StringRemove() => SampleString.Remove(0, 1);

        public void AssertTestIsValid()
        {
            string subsRes = StringSubstring();
            string remvRes = StringRemove();

            if (subsRes == null
                || subsRes.Length != SampleString.Length - 1
                || subsRes != remvRes) {
                throw new Exception("INVALID TEST!");
            }
        }
    }

    class Program
    {
        static void Main()
        {
            // let's make sure test results are really equal / valid
            new StringSubstringVsRemove().AssertTestIsValid();

            var summary = BenchmarkRunner.Run<StringSubstringVsRemove>();
        }
    }
}

Resultados:

BenchmarkDotNet=v0.11.4, OS=Windows 10.0.17763.253 (1809/October2018Update/Redstone5)
Intel Core i7-6700HQ CPU 2.60GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.0.100-preview-010184
  [Host]     : .NET Core 3.0.0-preview-27324-5 (CoreCLR 4.6.27322.0, CoreFX 4.7.19.7311), 64bit RyuJIT
  DefaultJob : .NET Core 3.0.0-preview-27324-5 (CoreCLR 4.6.27322.0, CoreFX 4.7.19.7311), 64bit RyuJIT

|          Method |     Mean |     Error |    StdDev |
|---------------- |---------:|----------:|----------:|
| StringSubstring | 19.37 ns | 0.3940 ns | 0.3493 ns |
|    StringRemove | 22.52 ns | 0.4062 ns | 0.3601 ns |
Nicholas Petersen
fuente
9

Supongo que Removey Substringhabría empate en el primer lugar, ya que ambos sorber una porción de tamaño fijo de la cadena, mientras que TrimStarthace una exploración desde la izquierda con una prueba en cada personaje y luego tiene que realizar exactamente el mismo trabajo que el Otros dos métodos. En serio, sin embargo, esto es dividir los pelos.

Marcelo Cantos
fuente
1
En realidad, Substringes más rápido que Remove, porque Removellama Substring.
Jaroslav Jandek
@Jaroslav: Esto no es cierto. Tanto Substringy Removeconfiar en un método privado, FillSubstring.
Marcelo Cantos
No lo verifiqué, pero suena muy plausible:string Remove(this string source, int from, int to) { return source.SubString(0, from) + source.SubString(to); }
Dykam
1
@Jaroslav: Estoy mirando el desarmado Reflector de los dos métodos en mscorlib.dll en un entorno de desarrollo de Windows bastante convencional. Ambos llaman System.PInvoke.EE.AllocateStringpara asignar el objeto de cadena de destino y luego llaman FillSubstringpara copiar caracteres. ¿Estoy mirando lo incorrecto?
Marcelo Cantos
1
@Marcelo: De todos modos, tu primer comentario originalmente decía algo completamente diferente. Probablemente debería haber usado una mejor redacción, aunque el punto es válido ( Substring> Remove). No voy a comentar más porque la discusión tomó suficiente tiempo.
Jaroslav Jandek
6

Podrías perfilarlo, si realmente te importara. Escriba un bucle de muchas iteraciones y vea qué sucede. Sin embargo, es probable que este no sea el cuello de botella en su aplicación, y TrimStart parece el más semánticamente correcto. Esfuércese por escribir el código de manera legible antes de optimizar.

Stefan Kendall
fuente
66
TrimStartes lo menos correcto, ya "//temp string".TrimStart('/')que no solo eliminará el primero '/'.
Marcelo Cantos
La función está mal nombrada entonces. No soy un chico de C #.
Stefan Kendall
@StefanKendall: Mira las etiquetas
Vijay Singh Rana