web-dev-qa-db-fra.com

StringBuilder / StringBuffer contre l'opérateur "+"

Je lis " Java meilleur, plus rapide et plus léger " (par Bruce Tate et Justin Gehtland) et je connais les exigences de lisibilité dans les équipes de type agile, comme ce que Robert Martin discute dans ses livres de codage propres. Dans l'équipe où je suis maintenant, on m'a dit explicitement de ne pas utiliser le +, car il crée des objets chaîne supplémentaires (et inutiles) pendant l'exécution.

Mais cela article , écrit en '04, explique comment l'allocation d'objet est d'environ 10 instructions machine. (essentiellement gratuit)

Il explique également comment le GC contribue également à réduire les coûts dans cet environnement.

Quels sont les compromis de performances réels entre l'utilisation de +, StringBuilder ou StringBuffer? (Dans mon cas, c'est StringBuffer uniquement car nous sommes limités à Java 1.4.2.)

StringBuffer pour moi se traduit par un code laid et moins lisible, comme le montrent quelques exemples dans le livre de Tate. Et StringBuffer est synchronisé avec les threads, ce qui semble avoir ses propres coûts qui l'emportent sur le "danger" de l'utilisation de + opérateur.

Réflexions/opinions?

35
avgvstvs

L'utilisation de la concaténation String est traduite en opérations StringBuilder par le compilateur.

Pour voir comment fonctionne le compilateur, je vais prendre un exemple de classe, le compiler et le décompiler avec jad pour voir quel est le bytecode généré.

Classe d'origine:

public void method1() {
    System.out.println("The answer is: " + 42);
}

public void method2(int value) {
    System.out.println("The answer is: " + value);
}

public void method3(int value) {
    String a = "The answer is: " + value;
    System.out.println(a + " what is the question ?");
}

La classe décompilée:

public void method1()
{
    System.out.println("The answer is: 42");
}

public void method2(int value)
{
    System.out.println((new StringBuilder("The answer is: ")).append(value).toString());
}

public void method3(int value)
{
    String a = (new StringBuilder("The answer is: ")).append(value).toString();
    System.out.println((new StringBuilder(String.valueOf(a))).append(" what is the question ?").toString());
}
  • Sur method1 le compilateur a effectué l'opération au moment de la compilation.
  • Sur method2 la concaténation String équivaut à utiliser manuellement StringBuilder.
  • Sur method3 la concaténation String est définitivement mauvaise car le compilateur crée un second StringBuilder plutôt que de réutiliser le précédent.

Donc ma règle simple est que les concaténations sont bonnes à moins que vous n'ayez besoin de concaténer à nouveau le résultat: par exemple dans des boucles ou lorsque vous devez stocker un résultat intermédiaire.

49
gabuzo

Votre équipe doit en savoir plus sur les raisons d'éviter la concaténation répétée de chaînes .

Il y a certainement fois quand il est logique d'utiliser StringBuffer - en particulier lorsque vous créez une chaîne dans une boucle, surtout si vous n'êtes pas sûr qu'il y aura peu d'itérations dans la boucle. Notez qu'il ne s'agit pas seulement de créer de nouveaux objets - il s'agit de copier toutes les données de texte que vous avez déjà ajoutées. Gardez également à l'esprit que l'allocation d'objets n'est "essentiellement gratuite" que si vous ne pensez pas à la récupération de place. Oui, s'il y a assez de place dans la génération actuelle, il s'agit essentiellement d'incrémenter un pointeur ... mais:

  • Cette mémoire a dû être effacée à un moment donné. Ce n'est pas gratuit.
  • Vous raccourcissez le temps jusqu'à ce que le prochain GC soit requis. GC n'est pas gratuit.
  • Si votre objet appartient à la prochaine génération, le nettoyage peut prendre plus de temps - encore une fois, il n'est pas gratuit.

Toutes ces choses sont raisonnablement bon marché dans la mesure où cela ne vaut "généralement" pas de plier un design loin de l'élégance pour éviter de créer des objets ... mais vous ne devriez pas ' t les considérer comme gratuits .

En revanche, il est inutile d'utiliser StringBuffer dans les cas où vous n'aurez pas besoin des chaînes intermédiaires. Par exemple:

String x = a + b + c + d;

est au moins aussi efficace que:

StringBuffer buffer = new StringBuffer();
buffer.append(a);
buffer.append(b);
buffer.append(c);
buffer.append(d);
String x = buffer.toString();
24
Jon Skeet

Pour les petites concaténations, vous pouvez simplement utiliser String et + dans un souci de lisibilité. La performance ne va pas en souffrir. Mais si vous faites beaucoup d'opérations de concaténation, optez pour StringBuffer.

5
adrianboimvaser