假设我们有以下字符串

string data= "/temp string";

如果我们想删除第一个字符/我们可以通过很多方法,例如:

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

但是,真的,我不知道哪一个有最好的算法,做得更快。 有一个是最好的还是所有都是一样的?


当前回答

第二个选项与其他选项不一样——如果字符串是"///foo",它将变成"foo"而不是"//foo"。

第一个选项比第三个选项需要更多的工作来理解-我认为Substring选项是最常见和可读的。

(显然,作为单独的语句,它们中的每一个都不会做任何有用的事情——你需要将结果分配给一个变量,可能是数据本身。)

I wouldn't take performance into consideration here unless it was actually becoming a problem for you - in which case the only way you'd know would be to have test cases, and then it's easy to just run those test cases for each option and compare the results. I'd expect Substring to probably be the fastest here, simply because Substring always ends up creating a string from a single chunk of the original input, whereas Remove has to at least potentially glue together a start chunk and an end chunk.

其他回答

如果你真的在乎,你可以侧写。编写一个多次迭代的循环,看看会发生什么。然而,这可能不是应用程序中的瓶颈,TrimStart似乎在语义上是最正确的。在优化之前,努力编写可读性强的代码。

第二个选项与其他选项不一样——如果字符串是"///foo",它将变成"foo"而不是"//foo"。

第一个选项比第三个选项需要更多的工作来理解-我认为Substring选项是最常见和可读的。

(显然,作为单独的语句,它们中的每一个都不会做任何有用的事情——你需要将结果分配给一个变量,可能是数据本身。)

I wouldn't take performance into consideration here unless it was actually becoming a problem for you - in which case the only way you'd know would be to have test cases, and then it's easy to just run those test cases for each option and compare the results. I'd expect Substring to probably be the fastest here, simply because Substring always ends up creating a string from a single chunk of the original input, whereas Remove has to at least potentially glue together a start chunk and an end chunk.

在。net Core中也是如此:

data = data[1..];

我知道这是一个超级优化的领域,但这似乎是一个很好的借口来踢BenchmarkDotNet的车轮。这个测试的结果(甚至在。net Core上)是Substring比Remove稍微快一点,在这个示例测试中:Remove的19.37ns vs 22.52ns。所以快了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 |

我猜Remove和Substring将并列第一,因为它们都占用了字符串的固定大小的部分,而TrimStart从左侧扫描并对每个字符进行测试,然后必须执行与其他两个方法完全相同的工作。但说真的,这是吹毛求疵。