Question

Quelle est la manière la plus efficace de concaténer des chaînes ?

Était-ce utile?

La solution

Le StringBuilder.Append() La méthode est bien meilleure que d’utiliser l’opérateur +.Mais j'ai découvert que lors de l'exécution de 1 000 concaténations ou moins, String.Join() est encore plus efficace que StringBuilder.

StringBuilder sb = new StringBuilder();
sb.Append(someString);

Le seul problème avec String.Join est que vous devez concaténer les chaînes avec un délimiteur commun.(Edit :) comme @ryanversaw l'a souligné, vous pouvez créer la chaîne de délimiteur vide.

string key = String.Join("_", new String[] 
{ "Customers_Contacts", customerID, database, SessionID });

Autres conseils

Rico Mariani, le gourou des performances .NET, avait un article sur ce même sujet.Ce n'est pas aussi simple qu'on pourrait le penser.Le conseil de base est le suivant :

Si votre modèle ressemble à :

x = f1(...) + f2(...) + f3(...) + f4(...)

c'est un concat et c'est zippy, StringBuilder n'aidera probablement pas.

Si votre modèle ressemble à :

if (...) x += f1(...)
if (...) x += f2(...)
if (...) x += f3(...)
if (...) x += f4(...)

alors vous voulez probablement StringBuilder.

Encore un autre article pour étayer cette affirmation vient d'Eric Lippert où il décrit les optimisations effectuées sur une ligne + concaténations de manière détaillée.

Il existe 6 types de concaténations de chaînes :

  1. En utilisant le plus (+) symbole.
  2. En utilisant string.Concat().
  3. En utilisant string.Join().
  4. En utilisant string.Format().
  5. En utilisant string.Append().
  6. En utilisant StringBuilder.

Dans une expérience, il a été prouvé que string.Concat() est la meilleure façon d'aborder si les mots sont inférieurs à 1000 (environ) et si les mots sont supérieurs à 1000 alors StringBuilder Devrait être utilisé.

Pour plus d'informations, vérifiez ceci site.

string.Join() contre string.Concat()

La méthode string.Concat ici est équivalente à l’invocation de la méthode string.Join avec un séparateur vide.Ajouter une chaîne vide est rapide, mais ne pas le faire est encore plus rapide, donc le chaîne.Concat la méthode serait supérieure ici.

Depuis Chinh Do - StringBuilder n'est pas toujours plus rapide:

Règles de base

  • Lors de la concaténation de trois valeurs de chaîne dynamique ou moins, utilisez la concaténation de chaîne traditionnelle.

  • Lors de la concaténation de plus de trois valeurs de chaîne dynamique, utilisez StringBuilder.

  • Lorsque vous construisez une grande chaîne à partir de plusieurs littéraux de chaîne, utilisez soit le littéral de chaîne @, soit l'opérateur + en ligne.

La plupart du moment, StringBuilder est votre meilleur pari, mais il existe des cas, comme indiqué dans cet article, dans lesquels vous devriez au moins réfléchir à chaque situation.

Si vous travaillez en boucle, StringBuilder est probablement la solution ;cela vous évite de devoir créer régulièrement de nouvelles chaînes.Cependant, dans un code qui ne s'exécutera qu'une seule fois, String.Concat convient probablement.

Cependant, Rico Mariani (gourou de l'optimisation .NET) j'ai inventé un quiz dans lequel il déclare à la fin que, dans la plupart des cas, il recommande String.Format.

Voici la méthode la plus rapide que j'ai développée en une décennie pour mon application PNL à grande échelle.J'ai des variantes pour IEnumerable<T> et d'autres types d'entrées, avec et sans séparateurs de différents types (Char, String), mais je montre ici le cas simple de concaténer toutes les chaînes d'un tableau en une seule chaîne, sans séparateur.La dernière version ici est développée et testée unitairement sur C#7 et .NET 4.7.

Il existe deux clés pour des performances supérieures :la première consiste à précalculer la taille totale exacte requise.Cette étape est triviale lorsque l'entrée est un tableau comme indiqué ici.Pour la manipulation IEnumerable<T> au lieu de cela, il vaut la peine de rassembler d'abord les chaînes dans un tableau temporaire pour calculer ce total (le tableau est requis pour éviter d'appeler ToString() plus d'une fois par élément puisque techniquement, étant donné la possibilité d'effets secondaires, cela pourrait changer la sémantique attendue d'une opération de « jointure de chaîne »).

Ensuite, étant donné la taille totale de l'allocation de la chaîne finale, l'augmentation la plus importante des performances est obtenue en construire la chaîne de résultat sur place.Pour ce faire, il faut recourir à la technique (peut-être controversée) qui consiste à suspendre temporairement l'immuabilité d'un nouveau système. String qui est initialement alloué plein de zéros.Cependant, une telle controverse mise à part...

... notez qu'il s'agit de la seule solution de concaténation en masse sur cette page qui évite entièrement un cycle supplémentaire d'attribution et de copie par le String constructeur.

Code complet :

/// <summary>
/// Concatenate the strings in 'rg', none of which may be null, into a single String.
/// </summary>
public static unsafe String StringJoin(this String[] rg)
{
    int i;
    if (rg == null || (i = rg.Length) == 0)
        return String.Empty;

    if (i == 1)
        return rg[0];

    String s, t;
    int cch = 0;
    do
        cch += rg[--i].Length;
    while (i > 0);
    if (cch == 0)
        return String.Empty;

    i = rg.Length;
    fixed (Char* _p = (s = new String(default(Char), cch)))
    {
        Char* pDst = _p + cch;
        do
            if ((t = rg[--i]).Length > 0)
                fixed (Char* pSrc = t)
                    memcpy(pDst -= t.Length, pSrc, (UIntPtr)(t.Length << 1));
        while (pDst > _p);
    }
    return s;
}

[DllImport("MSVCR120_CLR0400", CallingConvention = CallingConvention.Cdecl)]
static extern unsafe void* memcpy(void* dest, void* src, UIntPtr cb);

Je dois mentionner que ce code présente une légère modification par rapport à ce que j'utilise moi-même.Dans l'original, je appeler le cpblk Instruction IL depuis C# pour faire la copie proprement dite.Pour plus de simplicité et de portabilité dans le code ici, j'ai remplacé cela par P/Invoke memcpy au lieu de cela, comme vous pouvez le constater.Pour des performances optimales sur x64 (mais peut-être pas x86), vous souhaiterez peut-être utiliser le cpblk méthode à la place.

De ceci Article MSDN:

Il existe des frais généraux associés à la création d'un objet StringBuilder, à la fois dans le temps et la mémoire.Sur une machine avec une mémoire rapide, un stringbuilder en vaut la peine si vous effectuez environ cinq opérations.En règle générale, je dirais que 10 opérations de cordes ou plus sont une justification de la surcharge sur n'importe quelle machine, même plus lente.

Donc, si vous faites confiance à MSDN, utilisez StringBuilder si vous devez effectuer plus de 10 opérations/concaténations de chaînes – sinon une simple concaténation de chaîne avec « + » convient.

En ajoutant aux autres réponses, gardez à l'esprit que StringBuilder peut recevoir une quantité initiale de mémoire à allouer.

Le capacité Le paramètre définit le nombre maximum de caractères pouvant être stockés dans la mémoire allouée par l'instance actuelle.Sa valeur est attribuée au Capacité propriété.Si le nombre de caractères à stocker dans l'instance actuelle dépasse cette valeur capacité valeur, l'objet StringBuilder alloue de la mémoire supplémentaire pour les stocker.

Si capacité est zéro, la capacité par défaut spécifique à l'implémentation est utilisée.

L'ajout répété de chaînes à un StringBuilder qui n'a pas été pré-alloué peut entraîner de nombreuses allocations inutiles, tout comme la concaténation répétée de chaînes normales.

Si vous savez quelle sera la longueur de la chaîne finale, si vous pouvez la calculer de manière triviale ou si vous pouvez faire une supposition éclairée sur le cas courant (en allouer trop n'est pas nécessairement une mauvaise chose), vous devriez fournir cette information au constructeur ou au Capacité propriété. En particulier lors de l'exécution de tests de performances pour comparer StringBuilder avec d'autres méthodes telles que String.Concat, qui font la même chose en interne.Tout test que vous voyez en ligne et qui n'inclut pas la pré-allocation StringBuilder dans ses comparaisons est erroné.

Si vous ne pouvez pas deviner la taille, vous êtes probablement en train d'écrire une fonction utilitaire qui devrait avoir son propre argument facultatif pour contrôler la pré-allocation.

Il est également important de souligner que vous devez utiliser le + opérateur si vous concaténez chaînes littérales.

Lorsque vous concaténez des chaînes littérales ou des constantes de chaîne à l’aide de l’opérateur +, le compilateur crée une seule chaîne.Aucune concaténation au moment de l’exécution ne se produit.

Comment:Concaténer plusieurs chaînes (Guide de programmation C#)

Voici peut-être une autre solution alternative pour concaténer plusieurs chaînes.

String str1 = "sometext";
string str2 = "some other text";

string afterConcate = $"{str1}{str2}";

interpolation de chaîne

Le plus efficace est d'utiliser StringBuilder, comme ceci :

StringBuilder sb = new StringBuilder();
sb.Append("string1");
sb.Append("string2");
...etc...
String strResult = sb.ToString();

@jonezy :String.Concat convient si vous avez quelques petites choses.Mais si vous concaténez des mégaoctets de données, votre programme risque de s'effondrer.

Essayez ces 2 morceaux de code et vous trouverez la solution.

 static void Main(string[] args)
    {
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < 10000000; i++)
        {
            s.Append( i.ToString());
        }
        Console.Write("End");
        Console.Read();
    }

Contre

static void Main(string[] args)
    {
        string s = "";
        for (int i = 0; i < 10000000; i++)
        {
            s += i.ToString();
        }
        Console.Write("End");
        Console.Read();
    }

Vous constaterez que le premier code se terminera très rapidement et que la mémoire sera importante.

Le deuxième code peut-être que la mémoire sera bonne, mais cela prendra plus de temps...beaucoup plus longtemps.Donc si vous avez une application pour beaucoup d’utilisateurs et que vous avez besoin de rapidité, utilisez la 1ère.Si vous avez une application pour une application mono-utilisateur à court terme, vous pouvez peut-être utiliser les deux ou la seconde sera plus "naturelle" pour les développeurs.

Acclamations.

System.String est immuable.Lorsque nous modifions la valeur d'une variable chaîne, une nouvelle mémoire est allouée à la nouvelle valeur et l'allocation de mémoire précédente est libérée.System.StringBuilder a été conçu pour avoir le concept d'une chaîne mutable dans laquelle diverses opérations peuvent être effectuées sans attribuer d'emplacement mémoire séparé pour la chaîne modifiée.

Une autre solution:

à l'intérieur de la boucle, utilisez List au lieu de string.

List<string> lst= new List<string>();

for(int i=0; i<100000; i++){
    ...........
    lst.Add(...);
}
return String.Join("", lst.ToArray());;

c'est très très rapide.

Cela dépend vraiment de votre modèle d'utilisation.Un benchmark détaillé entre string.Join, string,Concat et string.Format peut être trouvé ici : String.Format ne convient pas à la journalisation intensive

(C'est en fait la même réponse que celle que j'ai donnée à ce question)

Pour seulement deux chaînes, vous ne souhaitez certainement pas utiliser StringBuilder.Il existe un certain seuil au-dessus duquel la surcharge de StringBuilder est inférieure à la surcharge liée à l'allocation de plusieurs chaînes.

Donc, pour plus de 2-3 chaînes, utilisez Code de DannySmurf.Sinon, utilisez simplement l'opérateur +.

Cela dépendrait du code.StringBuilder est généralement plus efficace, mais si vous ne concaténez que quelques chaînes et que vous faites tout cela sur une seule ligne, les optimisations de code s'en chargeront probablement pour vous.Il est également important de réfléchir à l'apparence du code :pour les ensembles plus grands, StringBuilder facilitera la lecture, pour les plus petits, StringBuilder ajoutera simplement un encombrement inutile.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top