从字符串中删除第一个字符的最快方法

274

假设我们有以下字符串:

string data= "/temp string";

如果我们想要删除第一个字符/,可以通过很多方式来实现,例如:

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

但是,实际上我不知道哪个算法最好并且执行速度更快..
有一个算法是最好的还是所有算法都一样?


您是否想要无论如何删除第一个字符,还是需要检查该字符是否确实为“/”? - SRKX
8
TrimStart 不会删除第一个字符,它会从开头删除 n 个字符。Substring 是最快的。 - Jaroslav Jandek
我只需要删除任何第一个字符。 - Amr Badawy
6
如果你要删除任何第一个字符,那么TrimStart()完全行不通。 - BoltClock
1
@BoltClock:是的,那就是我说的(打的)。 - Jaroslav Jandek
5个回答

185

第二个选项与其他选项真的不同 - 如果字符串是“///foo”,它将变为“foo”,而不是“//foo”。

与第三个选项相比,第一个选项需要更多的工作才能理解 - 我认为Substring选项最常见和易读。

(显然,每个选项作为单独语句都不会有任何有用的效果 - 您需要将结果赋给变量,可能是data本身。)

我不会考虑性能,除非它实际上对您造成了问题 - 在这种情况下,唯一知道的方法就是拥有测试用例,然后很容易为每个选项运行这些测试用例并比较结果。我期望Substring在这里可能是最快的,因为Substring总是从原始输入的单个块中创建字符串,而Remove则必须至少可能地将起始块和结束块粘合在一起。


44
我现在通过拨打每个号码进行检查,得出以下结果: Remove : 06.63 - TrimStart : 04.71 - subString : 03.09因此,从结果来看,subString 是最好的。 - Amr Badawy
5
记住,当你以这种方式测试性能时,你会受到CPU缓存的影响,因此你需要在预先填充了数组(列表)的随机字符串上进行测试,并随机选择该数组(列表)的元素。 - ajeh

26

我知道这是超级优化的领域,但这似乎是一个很好的理由来测试一下 BenchmarkDotNet。在这个测试中(甚至在.NET Core上),SubstringRemove 略微快一些,在这个样本测试中:19.37ns 对比 Remove 的 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 |

17
在.Net Core中,这也可以工作:
data = data[1..];

10

我猜RemoveSubstring可能并列第一,因为它们都会截取字符串的一个固定长度,而TrimStart则需要从左到右逐个检查每个字符,然后再执行与前两种方法完全相同的操作。不过,这已经是深入探讨了。


1
实际上,“Substring”比“Remove”快,因为“Remove”调用了“Substring”。 - Jaroslav Jandek
@Jaroslav:这不是真的。SubstringRemove都依赖于一个私有方法FillSubstring - Marcelo Cantos
没有验证过,但听起来很有道理:string Remove(this string source, int from, int to) { return source.SubString(0, from) + source.SubString(to); } - Dykam
1
@JaroslavпјҡжҲ‘жӯЈеңЁдёҖдёӘзӣёеҪ“дј з»ҹзҡ„WindowsејҖеҸ‘зҺҜеўғдёӯжҹҘзңӢmscorlib.dllдёӯдёӨдёӘж–№жі•зҡ„еҸҚзј–иҜ‘гҖӮе®ғ们йғҪи°ғз”ЁSystem.PInvoke.EE.AllocateStringжқҘеҲҶй…Қзӣ®ж Үеӯ—з¬ҰдёІеҜ№иұЎпјҢ然еҗҺи°ғз”ЁFillSubstringжқҘеӨҚеҲ¶еӯ—з¬ҰгҖӮжҲ‘жҳҜеңЁзңӢй”ҷдәҶеҗ—пјҹ - Marcelo Cantos
1
@Marcelo:无论如何,你最初的评论原本说了完全不同的事情。我可能应该使用更好的措辞,但重点是有效的(Substring > Remove)。我不会再发表评论了,因为这个讨论已经占用了我足够的时间。 - Jaroslav Jandek
显示剩余3条评论

6

如果您真的关心,可以对其进行性能分析。编写多次迭代的循环并观察发生的情况。但很有可能这不是您应用程序中的瓶颈,TrimStart似乎是最符合语义的选项。在优化之前,努力编写可读的代码。


7
“TrimStart”并不是很准确,因为“'//temp string'.TrimStart('/')”并不能仅仅移除第一个“'/'”。 - Marcelo Cantos
这个函数的命名不太合适。我不是 C# 的专家。 - Stefan Kendall
@StefanKendall:看一下标签。 - Vijay Singh Rana

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接