Moyen le plus rapide pour supprimer le premier caractère d'une chaîne

207

Disons que nous avons la chaîne suivante

string data= "/temp string";

Si nous voulons supprimer le premier caractère, /nous pouvons le faire de nombreuses manières, telles que:

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

Mais, vraiment, je ne sais pas lequel a le meilleur algorithme et le fait plus rapidement. Y en a -
t-il un qui est le meilleur ou tous sont les mêmes?

Amr Badawy
la source
Voulez-vous quand même supprimer le premier caractère ou devez-vous vérifier que ce caractère est bien un /?
SRKX
5
TrimStartne supprimera pas le premier caractère, il supprimera les ncaractères du début. Substringest le plus rapide.
Jaroslav Jandek
j'ai juste besoin de supprimer tout premier caractère
Amr Badawy
6
Si vous supprimez un premier caractère, il TrimStart()est totalement hors de question.
BoltClock
@BoltClock: oui, c'est ce que j'ai dit (tapé).
Jaroslav Jandek

Réponses:

147

La deuxième option n'est vraiment pas la même que les autres - si la chaîne est "/// foo" elle deviendra "foo" au lieu de "// foo".

La première option nécessite un peu plus de travail pour comprendre que la troisième - je considérerais l' Substringoption comme la plus courante et la plus lisible.

(Évidemment, chacun d'eux en tant qu'instruction individuelle ne fera rien d'utile - vous devrez attribuer le résultat à une variable, peut data- être elle - même.)

Je ne prendrais pas en compte les performances ici à moins que cela ne devienne réellement un problème pour vous - dans ce cas, la seule façon dont vous le sauriez serait d'avoir des cas de test, puis il est facile de simplement exécuter ces cas de test pour chaque option et comparer les résultats. Je m'attendrais Substringà être probablement le plus rapide ici, tout simplement parce qu'il Substringfinit toujours par créer une chaîne à partir d'un seul morceau de l'entrée d'origine, alors qu'il Removedoit au moins potentiellement coller ensemble un morceau de début et un morceau de fin.

Jon Skeet
la source
36
Je vérifie maintenant en appelant chacun environ 90000000 et je passe le résultat suivant: Remove: 06.63 - TrimStart: 04.71 - subString: 03.09 donc du résultat, la sous-chaîne est la meilleure
Amr Badawy
5
N'oubliez pas que lorsque vous testez les performances de cette manière, vous êtes affecté par la mise en cache du processeur, vous devez donc le faire sur les chaînes aléatoires, que vous avez pré-rempli un tableau (liste) avec, et sélectionnez au hasard l'élément de ce tableau ( liste).
ajeh
12

Je sais que c'est une terre d'hyper-optimisation, mais cela semblait être une bonne excuse pour lancer les roues BenchmarkDotNet. Le résultat de ce test (sur .NET Core même) est Substringtoujours légèrement plus rapide que Removedans cet exemple de test: 19,37 ns contre 22,52 ns pour Remove. Donc, environ 16% plus rapidement.

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

Résultats:

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
la source
9

Je devinerais cela Removeet Substringj'attacherais pour la première place, car ils absorbent tous les deux une partie de taille fixe de la chaîne, tandis TrimStartqu'un balayage à partir de la gauche avec un test sur chaque caractère et doit ensuite effectuer exactement le même travail que le deux autres méthodes. Sérieusement, cependant, cela divise les cheveux.

Marcelo Cantos
la source
1
En fait, Substringest plus rapide que Remove, parce que les Removeappels Substring.
Jaroslav Jandek
@Jaroslav: Ce n'est pas vrai. Les deux Substringet Removecompter sur une méthode privée, FillSubstring.
Marcelo Cantos
Je ne l'ai pas vérifié, mais cela semble très plausible:string Remove(this string source, int from, int to) { return source.SubString(0, from) + source.SubString(to); }
Dykam
1
@Jaroslav: Je regarde le démontage du réflecteur des deux méthodes dans mscorlib.dll sur un environnement de développement Windows assez conventionnel. Ils appellent tous les deux System.PInvoke.EE.AllocateStringpour allouer l'objet chaîne de destination, puis appellent FillSubstringpour copier des caractères. Suis-je en train de regarder la mauvaise chose?
Marcelo Cantos
1
@Marcelo: Quoi qu'il en soit, votre premier commentaire disait à l'origine une chose totalement différente. J'aurais probablement dû utiliser une meilleure formulation, le point est cependant valable ( Substring> Remove). Je ne vais pas commenter davantage car la discussion a pris assez de temps.
Jaroslav Jandek
6

Vous pouvez le profiler si vous vous en souciez vraiment. Écrivez une boucle de nombreuses itérations et voyez ce qui se passe. Il y a cependant des chances que ce ne soit pas le goulot d'étranglement dans votre application, et TrimStart semble le plus sémantiquement correct. Efforcez-vous d'écrire le code de manière lisible avant d'optimiser.

Stefan Kendall
la source
6
TrimStartest le moins correct, car "//temp string".TrimStart('/')ne pas supprimer tout simplement le premier '/'.
Marcelo Cantos
La fonction est alors mal nommée. Je ne suis pas un gars C #.
Stefan Kendall
@StefanKendall: Regardez les tags
Vijay Singh Rana