Maneira mais rápida de remover o primeiro caractere em uma String

207

Digamos que temos a seguinte string

string data= "/temp string";

Se queremos remover o primeiro caractere /, podemos fazê-lo de várias maneiras, como:

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

Mas, realmente, não sei qual deles tem o melhor algoritmo e faz isso mais rápido.
Existe um que seja o melhor ou todos são iguais?

Amr Badawy
fonte
Deseja remover o primeiro caractere de qualquer maneira ou precisa verificar se esse caractere é realmente um /?
SRKX
5
TrimStartnão removerá o primeiro caractere, mas removerá os ncaracteres desde o início. Substringé o mais rápido.
Jaroslav Jandek
Eu só preciso remover qualquer primeiro caractere
Amr Badawy
6
Se você está removendo qualquer primeiro personagem, TrimStart()está completamente fora de questão.
BoltClock
@BoltClock: sim, foi o que eu disse (digitado).
Jaroslav Jandek

Respostas:

147

A segunda opção realmente não é a mesma que as outras - se a string for "/// foo", ela se tornará "foo" em vez de "// foo".

A primeira opção precisa de um pouco mais de trabalho do que a terceira - eu consideraria a Substringopção a mais comum e legível.

(Obviamente, cada um deles como uma declaração individual não fará nada de útil - você precisará atribuir o resultado a uma variável, possivelmente a datasi mesma.)

Eu não levaria em consideração o desempenho aqui, a menos que isso estivesse se tornando um problema para você. Nesse caso, a única maneira que você saberia seria ter casos de teste e, em seguida, é fácil executar esses casos de teste para cada opção e compare os resultados. Eu Substringprovavelmente seria o mais rápido aqui, simplesmente porque Substringsempre acaba criando uma sequência a partir de um único pedaço da entrada original, enquanto Removeque pelo menos potencialmente deve colar um pedaço inicial e um final.

Jon Skeet
fonte
36
Eu faço verificar agora pela chamada cada um sobre 90000000 e eu ir o seguinte resultado: Remover: 06,63 - TrimStart: 04,71 - CadeiaDeOrigem: 03.09 modo de substring resultado é o melhor
Amr Badawy
5
Lembre-se de que, ao testar o desempenho dessa maneira, você é afetado pelo cache da CPU; portanto, é necessário fazer isso nas seqüências aleatórias, preencher previamente uma matriz (lista) e selecionar aleatoriamente o elemento dessa matriz ( Lista).
ajeh 9/09/16
12

Sei que essa é uma área de hiper otimização, mas parecia uma boa desculpa para chutar as rodas BenchmarkDotNet. O resultado desse teste (inclusive no .NET Core) é que Substringé um pouco mais rápido do que Remove, neste teste de exemplo: 19,37ns vs 22,52ns para Remove. Então, cerca de 16% mais 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
fonte
9

Eu acho que Removee Substringseria o empate para o primeiro lugar, uma vez que ambos Slurp-se uma porção de tamanho fixo da corda, enquanto TrimStartfaz uma varredura da esquerda com um teste em cada personagem e, em seguida, tem de executar exatamente o mesmo trabalho que o outros dois métodos. Sério, porém, isso está dividindo os cabelos.

Marcelo Cantos
fonte
1
Na verdade, Substringé mais rápido que Remove, porque Removeliga Substring.
Jaroslav Jandek
@ Jaroslav: Isso não é verdade. Ambos Substringe Removedependem de um método privado FillSubstring,.
Marcelo Cantos
Não verificar-lo, mas muito parece plausível:string Remove(this string source, int from, int to) { return source.SubString(0, from) + source.SubString(to); }
dykam
1
@ Jaroslav: Estou olhando para a desmontagem do refletor dos dois métodos no mscorlib.dll em um ambiente de desenvolvimento Windows bastante convencional. Ambos chamam System.PInvoke.EE.AllocateStringpara alocar o objeto de sequência de destino e, em seguida, chamam FillSubstringpara copiar caracteres. Estou olhando para a coisa errada?
Marcelo Cantos
1
@ Marcelo: Enfim, seu primeiro comentário disse originalmente uma coisa totalmente diferente. Eu provavelmente deveria ter usado uma redação melhor, o argumento é válido ( Substring> Remove). Não vou comentar mais porque a discussão levou bastante do meu tempo.
Jaroslav Jandek
6

Você poderia criar um perfil, se realmente se importasse. Escreva um loop de muitas iterações e veja o que acontece. As chances são, no entanto, de que esse não seja o gargalo no seu aplicativo e o TrimStart parece o mais semanticamente correto. Esforce-se para escrever código de forma legível antes de otimizar.

Stefan Kendall
fonte
6
TrimStarté o menos correto, já que "//temp string".TrimStart('/')irá não apenas remover o primeiro '/'.
Marcelo Cantos
A função é mal nomeada então. Eu não sou um cara de C #.
27513 Stefan Kendall
@StefanKendall: Look at tags
Vijay Singh Rana