Question

Je me suis souvent posé la question: existe-t-il un coût en performances lié au fractionnement d'une chaîne sur plusieurs lignes afin d'améliorer la lisibilité lors de l'attribution initiale d'une valeur à une chaîne? Je sais que les chaînes sont immuables et qu'une nouvelle chaîne doit donc être créée à chaque fois. En outre, le coût des performances est en réalité hors de propos grâce au matériel très rapide d'aujourd'hui (à moins que vous ne soyez dans une boucle diabolique). Ainsi, par exemple:

String newString = "This is a really long long long long long" +
    " long long long long long long long long long long long long " +
    " long long long long long long long long long string for example.";

Comment le compilateur de la JVM ou .Net et les autres optimisations le gèrent-ils. Va-t-il créer une seule chaîne? Ou créera-t-il 1 chaîne puis une nouvelle concaténation de la valeur puis une autre concaténant à nouveau les valeurs?

Ceci est pour ma propre curiosité.

Était-ce utile?

La solution

Cela est garanti par la spécification C # identique à la création de la chaîne dans un seul littéral, car il s'agit d'une constante à la compilation. De la section 7.18 de la spécification C # 3:

  

Chaque fois qu'une expression remplit les   exigences énumérées ci-dessus,   l'expression est évaluée à   temps de compilation. C’est vrai même si le   expression est une sous-expression d'un   expression plus grande qui contient   constructions non constantes.

(Voir les spécifications pour connaître les détails exacts des "conditions requises ci-dessus":)

La spécification du langage Java le spécifie au bas de section 3.10.5 :

  

Chaînes calculées par constante   expressions (§15.28) sont calculées à   compiler le temps et ensuite traité comme si   ils étaient littéraux.

Autres conseils

En effet, en Java, le compilateur transformera la String en constante.

class LongLongString
{
    public LongLongString()
    {
        String newString = "This is a really long long long long long" +
            " long long long long long long long long long long long long " +
            " long long long long long long long long long string for example.";
    }

    public static void main(String[] args)
    {
        new LongLongString();
    }
}

est compilé dans:

Compiled from "LongLongString.java"
class LongLongString extends java.lang.Object{
public LongLongString();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   ldc #2; //String This is a really long long long long long long long long long long long long long long long long long  long long long long long long long long long string for example.
   6:   astore_1
   7:   return

public static void main(java.lang.String[]);
  Code:
   0:   new #3; //class LongLongString
   3:   dup
   4:   invokespecial   #4; //Method "<init>":()V
   7:   pop
   8:   return

}

Comme on peut le constater, une seule ligne est chargée dans la ligne 4 au lieu de plusieurs instances String chargées.

Modifier: Le fichier source a été compilé à l'aide de javac version 1.6.0_06. En regardant Spécification du langage Java, troisième édition , (et la même section mentionnée dans Réponse de Jon Skeet ), je n’ai trouvé aucune référence permettant de déterminer si un compilateur doit concaténer une Chaîne dans une seule Chaîne , ce comportement est donc probablement spécifique à l’implémentation du compilateur.

Testez-le vous-même. En code C # (un équivalent Java fonctionnerait aussi):

string x = "A" + "B" + "C";
string y = "ABC";

bool same = object.ReferenceEquals(x, y); // true

Vous verrez que le résultat est true .

En passant, vous verrez que la chaîne est également internée dans le pool de chaînes du moteur d'exécution:

bool interned = object.ReferenceEquals(x, string.Intern(x)); // true

Aucun compromis sur les performances. L’optimisation du compilateur le fusionnera en une seule chaîne (au moins en Java).

Autant que je me souvienne, cela ne créera pas plusieurs chaînes, mais celle-ci.

L'équivalent .NET IL complémentaire de réponse de coobird :

Pour le code C #:

string s = "This is a really long long long long long" +
    " long long long long long long long long long long long long " +
    " long long long long long long long long long string for example.";
Console.WriteLine(s);

Une compilation de débogage produit:

.method public hidebysig static void Main(string[] args) cil managed
{
  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
  .maxstack 1
  .locals init (
      [0] string str)
  L_0000: ldstr "This is a really long long long long long long long long long long long long long long long long long  long long long long long long long long long string for example."
  L_0005: stloc.0 
  L_0006: ldloc.0 
  L_0007: call void [mscorlib]System.Console::WriteLine(string)
  L_000c: ret 
}

Donc, comme vous pouvez le constater, il s’agit d’une chaîne.

Tant que toutes les chaînes sont constantes (comme dans votre exemple), en Java (et j'imagine C #), le compilateur convertit cette chaîne en une seule chaîne.

Vous n'obtenez des problèmes de performance avec + que si vous concaténez beaucoup de chaînes dynamiques, comme dans une boucle. Dans ce cas, utilisez un StringBuilder ou StringBuffer.

Avertissement: Ceci est vrai pour Java. Je suppose que c'est vrai pour c #

Non seulement javac créera une seule chaîne, mais la machine virtuelle Java utilisera une chaîne pour toutes les autres chaînes contenant le même texte.

String a = "He" + "llo th"+ "ere";
String b = "Hell" + "o the"+ "re";
String c = "Hello" +" "+"there";
assert a == b; // these are the same String object.
assert a == c; // these are the same String object.

Remarque: ils seront le même objet String au moment de l'exécution même s'ils appartiennent à des classes différentes dans des fichiers JARS différents, compilés par différents compilateurs.

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