Najszybszy sposób na usunięcie pierwszego znaku z ciągu


207

Powiedzmy, że mamy następujący ciąg

string data= "/temp string";

Jeśli chcemy usunąć pierwszą postać, /możemy to zrobić na wiele sposobów, takich jak:

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

Ale tak naprawdę nie wiem, który ma najlepszy algorytm i robi to szybciej.
Czy jest taki, który jest najlepszy, czy wszystkie są takie same?


Czy mimo to chcesz usunąć pierwszą postać, czy też musisz sprawdzić, czy ta postać rzeczywiście jest /?
SRKX

5
TrimStartnie usunie pierwszego znaku, usunie nznaki od początku. Substringjest najszybszy.
Jaroslav Jandek

Muszę tylko usunąć pierwszą postać
Amr Badawy,

6
Jeśli usuwasz jakikolwiek pierwszy znak, TrimStart()jest całkowicie wykluczone.
BoltClock

@BoltClock: tak, właśnie to powiedziałem (wpisałem).
Jaroslav Jandek

Odpowiedzi:


147

Druga opcja tak naprawdę nie jest taka sama jak inne - jeśli ciąg ma postać „/// foo”, zmieni się w „foo” zamiast „// foo”.

Pierwsza opcja wymaga nieco więcej pracy, aby zrozumieć niż trzecia - uznałbym tę Substringopcję za najbardziej powszechną i czytelną.

(Oczywiście każde z nich jako osobna instrukcja nie przyniesie niczego pożytecznego - musisz przypisać wynik do zmiennej, być może datasamej).

Nie wziąłbym tutaj pod uwagę wydajności, chyba że stałoby się to dla ciebie problemem - w takim przypadku jedynym sposobem, aby wiedzieć, było posiadanie przypadków testowych, a następnie łatwo jest uruchomić te przypadki testowe dla każdej opcji i porównaj wyniki. Spodziewałbym się, że będę Substringprawdopodobnie najszybszy tutaj, po prostu dlatego, że Substringzawsze tworzy ciąg z pojedynczego fragmentu oryginalnego wejścia, podczas gdy Removemusi przynajmniej potencjalnie skleić fragment początkowy i końcowy.


36
Sprawdzam teraz, dzwoniąc do każdego o 90000000 i idę następujący wynik: Usuń: 06.63 - TrimStart: 04.71 - subString: 03.09 więc z podciągu wyników jest najlepszy
Amr Badawy

5
Pamiętaj tylko, że podczas testowania wydajności w ten sposób wpływa na ciebie buforowanie procesora, więc musisz to zrobić na losowych ciągach, które wcześniej wypełniłeś tablicę (listę) i losowo wybierasz element tej tablicy ( lista).
ajeh

12

Wiem, że to kraina hiperoptymalizacyjna, ale wydawało się, że to dobra wymówka, by kopać koła BenchmarkDotNet. Wynik tego testu (nawet w .NET Core) jest Substringnawet nieco szybszy niż Removew tym przykładowym teście: 19,37ns vs 22,52ns dla Remove. Więc jakieś ~ 16% szybciej.

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>();
        }
    }
}

Wyniki:

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 |

9

Sądzę, że Removei Substringremis na pierwszym miejscu, ponieważ obaj zasłaniają część łańcucha o stałej wielkości, podczas TrimStartgdy skanuje od lewej z testem na każdym znaku, a następnie musi wykonać dokładnie taką samą pracę jak pozostałe dwie metody. Poważnie, to jednak dzieli włosy.


1
W rzeczywistości Substringjest szybszy niż Remove, ponieważ Removepołączenia Substring.
Jaroslav Jandek

@Jaroslav: To nie jest prawda. Zarówno Substringi Removepolegać na prywatnej metodzie FillSubstring.
Marcelo Cantos,

Nie zweryfikowałem tego, ale brzmi to bardzo realistycznie:string Remove(this string source, int from, int to) { return source.SubString(0, from) + source.SubString(to); }
Dykam,

1
@Jaroslav: Patrzę na demontaż Reflectora dwóch metod w mscorlib.dll w dość konwencjonalnym środowisku programistycznym Windows. Obie wywołują, System.PInvoke.EE.AllocateStringaby przydzielić docelowy ciąg znaków, a następnie wywołują, FillSubstringaby skopiować znaki w poprzek. Czy patrzę na niewłaściwą rzecz?
Marcelo Cantos,

1
@Marcelo: W każdym razie, twój pierwszy komentarz początkowo mówił zupełnie inną rzecz. Prawdopodobnie powinienem był użyć lepszego sformułowania, ale to prawda ( Substring> Remove). Nie zamierzam komentować dalej, ponieważ dyskusja zajęła mi wystarczająco dużo czasu.
Jaroslav Jandek

6

Możesz to profilować, jeśli naprawdę ci na tym zależy. Napisz pętlę wielu iteracji i zobacz, co się stanie. Są jednak szanse, że nie jest to wąskim gardłem w twojej aplikacji, a TrimStart wydaje się najbardziej semantycznie poprawny. Staraj się pisać kod czytelnie przed optymalizacją.


6
TrimStartjest najmniej poprawne, ponieważ "//temp string".TrimStart('/')będzie nie tylko usunąć pierwszy '/'.
Marcelo Cantos,

Funkcja jest wtedy źle nazwana. Nie jestem facetem z C #.
Stefan Kendall,

@StefanKendall: Spójrz na tagi
Vijay Singh Rana
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.