Найшвидший спосіб видалення першого знака в рядку


207

Скажімо, у нас є наступний рядок

string data= "/temp string";

Якщо ми хочемо видалити перший символ, /ми можемо зробити це багатьма способами, такими як:

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

Але насправді я не знаю, який має найкращий алгоритм, і це робити швидше.
Чи є той, який найкращий, або всі однакові?


Ви хочете все-таки видалити перший символ або вам потрібно перевірити, чи справді цей символ є /?
SRKX

5
TrimStartне видалить перший знак, він видалить nсимволи з початку. Substringнайшвидший.
Ярослав Яндек

мені просто потрібно видалити будь-якого першого персонажа
Amr Badawy

6
Якщо ви видаляєте будь-якого першого персонажа, про TrimStart()це зовсім не йдеться.
BoltClock

@BoltClock: Так, це я сказав (набрав).
Ярослав Яндек

Відповіді:


147

Другий варіант насправді не такий, як інші - якщо рядок "/// foo", він стане "foo" замість "// foo".

Перший варіант потребує трохи більше роботи, щоб зрозуміти, ніж третій - я б розглядав Substringваріант як найбільш поширений і читабельний.

(Очевидно, що кожен із них як окремий вислів не принесе нічого корисного - вам потрібно буде призначити результат змінній, можливо, dataсамому.)

Я б не брав до уваги ефективність тут, якщо це насправді не стало проблемою для вас - у цьому випадку єдиним способом, який ви могли б знати, було б мати тестові випадки, і тоді легко просто запустити ці тестові справи для кожного варіанту та порівняйте результати. Я б очікував, Substringщо, мабуть, тут буде найшвидший, просто тому, що Substringзавжди закінчується створення рядка з одного шматка оригінального введення, тоді як Removeповинен хоча б потенційно склеїти стартовий і кінцевий шматок.


36
Я перевіряю зараз, зателефонувавши кожному по 90000000, і я перейду до наступного результату: Видалити: 06.63 - TrimStart: 04.71 - subString: 03.09, тому з підрядок результатів найкраще
Amr Badawy

5
Пам'ятайте лише, що коли ви тестуєте продуктивність таким чином, на вас впливає кешування процесора, тому вам потрібно зробити це на випадкових рядках, за допомогою яких ви попередньо заповнили масив (список) і випадковим чином обрали елемент цього масиву ( список).
ajeh

12

Я знаю, що це гіпер-оптимізація землі, але це здавалося гарним приводом для того, щоб розігнати колеса BenchmarkDotNet. Результат цього тесту (навіть на .NET Core) полягає в тому, що Substringвін настільки дещо швидший, ніж Removeу цьому вибірковому тесті: 19,37ns проти 22,52ns for Remove. Так деякі ~ 16% швидше.

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

Результати:

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

Я б здогадався, що це Removeі Substringбуде прив'язувати до першого місця, оскільки вони обидва балакають частину рядка фіксованого розміру, тоді TrimStartяк сканування зліва проводиться з тестом на кожен символ, а потім має виконати точно таку ж роботу, як і інші два методи. Однак, якщо це серйозно, це розщеплення волосся.


1
Насправді, Substringшвидше Remove, тому що Removeдзвонить Substring.
Ярослав Яндек

@Jaroslav: Це неправда. Обидва Substringі Removeпокладаються на приватний метод FillSubstring.
Marcelo Cantos

Не перевірили, але це звучить правдоподібно:string Remove(this string source, int from, int to) { return source.SubString(0, from) + source.SubString(to); }
Dykam

1
@Jaroslav: Я дивлюсь на розбирання рефлектора двох методів у mscorlib.dll на досить звичайному середовищі розробників Windows. Вони обидва закликають System.PInvoke.EE.AllocateStringвиділити об'єкт рядка призначення, а потім зателефонують FillSubstringскопіювати символи поперек. Я дивлюся на неправильну річ?
Марсело Кантос

1
@Marcelo: У будь-якому разі, ваш перший коментар спочатку сказав зовсім іншу річ. Я, мабуть, мав би скористатися кращим формулюванням, пункт дійсний, хоча ( Substring> Remove). Я не збираюся коментувати далі, тому що дискусія зайняла достатньо мого часу.
Ярослав Яндек

6

Ви могли б профайлювати його, якщо справді піклуєтесь. Напишіть цикл з багатьох ітерацій і подивіться, що відбувається. Однак є ймовірність, що це не вузьке місце у вашій програмі, і TrimStart здається найбільш семантично правильним. Намагайтеся писати код з легкістю перед оптимізацією.


6
TrimStartє найменш правильним, оскільки не"//temp string".TrimStart('/') буде просто видаляти перше . '/'
Марсело Кантос

Тоді функція погано названа. Я не хлопець C #.
Стефан Кендалл

@StefanKendall: Подивіться на теги
Віджай Сінгх Рана
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.