StringBuilder vs concaténation de Chaîne dans toString() en Java
Donné le 2 toString()
implémentations ci-dessous, qui est préférée:
public String toString(){
return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
}
ou
public String toString(){
StringBuilder sb = new StringBuilder(100);
return sb.append("{a:").append(a)
.append(", b:").append(b)
.append(", c:").append(c)
.append("}")
.toString();
}
?
Plus important, étant donné que nous n'avons que 3 biens qu'il ne peut pas faire une différence, mais à quel point voulez-vous passer de +
concat à StringBuilder
?
- À quel point pensez-vous passer à StringBuilder? Quand il les effets de la mémoire ou de la performance. Ou quand il peut. Si vous êtes vraiment seulement faire cela pour un couple de chaînes une fois, pas de soucis. Mais si vous allez faire encore et encore, vous devriez voir une différence mesurable lors de l'utilisation de StringBuilder.
- quelle est la moyenne de 100 en paramètre?
- 100 est la taille initiale de StringBuilder
- De sorte que le nombre de caractères maximum sera de 100?
- non seulement la taille initiale, si vous savez la taille approximative de la chaîne que vous faites affaire avec, alors vous pouvez dire
StringBuilder
la taille à allouer l'avance sinon il sera, si elle est à court d'espace, de doubler la taille par la création d'une nouvellechar[]
tableau puis copier les données de plus - ce qui est coûteux. Vous pouvez tricher en donnant la taille et il n'est pas nécessaire pour cette création de la matrice - donc, si vous pensez que votre chaîne sera de ~100 caractères vous pouvez ensuite régler le StringBuilder de cette taille et il ne sera jamais amené à développer en interne. - Documents connexes: stackoverflow.com/documentation/java/109/strings/5280/...
- c'est même intellij-support.jetbrains.com/hc/en-us/community/posts/...
Vous devez vous connecter pour publier un commentaire.
La Version 1 est préférable car il est plus court et le compilateur va en effet tourner vers la version 2 - pas de différence de performances que ce soit.
Au point où vous êtes la concaténation dans une boucle - c'est généralement lorsque le compilateur ne peut pas se substituer
StringBuilder
par lui-même.To increase the performance of repeated string concatenation, a Java compiler _may_ use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.
Le mot-clé qu'il soit peut. Étant donné que c'est officiellement en option (bien que probablement mis en œuvre) devrions-nous pas nous protéger?StringBuilder
est plus rapide, ne nous en avons besoin dans Java 8, etc... dans le détail. Recommande fortement.StringBuilder
est initialisé avec une capacité de 16. Si la dynamique arguments sont assez longues, alors la totalité de la chaîne de caractères qui sera peut-être plus longue, auquel cas chaque ajout nécessiterait interne de la matrice de redimensionnement, ce qui aurait une incidence sur les performances. La deuxième approche, cependant, initialise le générateur avec une capacité de 100, ce qui augmente de beaucoup la durée potentielle d'arguments imprimé, et n'a donc pas d'impact sur les performances si plus de cas.La clé est de savoir si vous êtes l'écriture d'une simple concaténation de tous dans un endroit, ou accumuler au fil du temps.
Pour l'exemple que vous avez donné, il n'y a aucun point en utilisant explicitement StringBuilder. (Regardez le code compilé pour votre premier cas.)
Mais si vous êtes la construction d'une chaîne de caractères par exemple à l'intérieur d'une boucle, utiliser StringBuilder.
De préciser, en supposant que hugeArray contient des milliers de chaînes de caractères, le code comme ceci:
beaucoup de temps et de mémoire de gaspillage par rapport à:
result += s;
ainsi (dans le premier exemple)"{a:"+ a + ", b:" + b + ", c: " + c +"}";
StringBuilder
l'instanciation, une série deappend
appels, et untoString
appel (la séquence exacte est trop long pour moi de coller dans un commentaire).javap
, vous pouvez analyser le bytecode avec elle) et, avec un peu modifié l'exemple (String str = ((a == null) ? a1 : a) + ((b == null) ? b1 : b) + ((c == c1) ? null : c);
), il produit encore des optimisations, en cours d'Exécution avec unjavac 1.8.0_102
.Que je préfère:
...parce que c'est court et facile à lire.
Je pas optimiser ce pour la vitesse, sauf si vous l'utilisez à l'intérieur d'une boucle avec un très grand nombre de répétitions et ont mesuré la différence de performances.
Je suis d'accord, que si vous avez à la sortie de beaucoup de paramètres, ce formulaire peut prêter à confusion (comme l'un des commentaires dire). Dans ce cas, j'aimerais passer à une forme plus lisible (peut-être en utilisant ToStringBuilder de apache commons - extrait de la réponse de matt b) et d'ignorer les performances de nouveau.
null
a+b
fait la même chose exacte queformat
sib
est null. Les deux méthodes se traduire par l'ajout de la chaîne de la valeur "null".null + "issimo"
produit "nullissimo".%s
ou params et à vérifier l'ensemble du code à l'aide de FindBugs ou de certains IDE fonctionnalité (comme pour les journaux).$"{a}, {b}, {c}"
. 🙂Dans la plupart des cas, vous ne pourrez pas voir une réelle différence entre les deux approches, mais il est facile de construire un scénario du pire des cas comme celui-ci:
La sortie est:
Le problème, c'est que pour += ajouter à une chaîne reconstruit une nouvelle chaîne, si il en coûte quelque chose de linéaire de la longueur de vos chaînes (somme des deux).
Donc à votre question:
La deuxième approche serait plus rapide, mais c'est moins lisible et plus difficile à maintenir.
Comme je l'ai dit, dans votre cas particulier, vous ne serait probablement pas voir la différence.
+=
, l'exemple d'origine était une séquence de+
, que le compilateur transforme d'un seulstring.concat
appel. Vos résultats ne s'appliquent pas.J'ai aussi eu un clash avec mon patron sur le fait de savoir si l'utilisation d'ajout ou +.Comme ils sont en utilisant Append(je ne peux pas comprendre comme ils le disent à chaque fois qu'un nouvel objet est créé).
J'ai donc pensé à faire quelques R&D. Bien que j'aime Michael Borgwardt explication mais je voulais juste montrer une explication si quelqu'un en aura vraiment besoin de savoir à l'avenir.
et le démontage de la classe ci-dessus en tant que
De ces deux codes, vous pouvez voir Michael est à droite.Dans chaque cas, une seule SB objet est créé.
Depuis Java 1.5, simple ligne d'enchainement avec "+" et StringBuilder.append() de générer exactement le même bytecode.
Donc pour le souci de la lisibilité du code, utilisez "+".
2 exceptions :
L'aide de la dernière version de Java(1.8) le démontage(
javap -c
) montre l'optimisation introduit par le compilateur.+
ainsisb.append()
va avoir le même code. Cependant, il vaut la peine d'inspecter le comportement si nous sommes à l'aide de+
dans une boucle for.Ajouter les chaînes de caractères à l'aide de + dans une boucle for
Java:
ByteCode:(
for
boucle extrait)Ajouter les chaînes de caractères à l'aide de stringbuilder.ajouter
Java:
ByteCdoe:(
for
boucle extrait)Il y a un peu de flagrante différence bien. Dans le premier cas, où
+
a été utilisé, de nouvellesStringBuilder
est créé pour chaque pour chaque itération de boucle et de résultat généré est stocké en faisant untoString()
appel(29 à 41). Donc, vous êtes la génération intermédiaire des Chaînes qui vos n'avez pas vraiment besoin tout en utilisant+
opérateur dansfor
boucle.En Java 9 de la version 1 devrait être plus rapide, car il est converti
invokedynamic
appel. Plus de détails peuvent être trouvés dans JEP-280:Pour des raisons de performance, l'utilisation de
+=
(String
concaténation) est déconseillé. La raison en est: JavaString
est immuable, chaque fois qu'un nouveau concaténation est fait un nouveauString
est créé (le nouveau est différent de l'empreinte digitale de l'ancienne déjà dans la Chaîne de la piscine ). La création de nouvelles chaînes met la pression sur la GC et ralentit le programme: création d'un objet est cher.Code ci-dessous devrait la rendre plus pratique et clair en même temps.
Résultats pour une course sont rapportés ci-dessous.
Ne pas considérer les résultats de 1 concaténation (JIT n'était pas encore fait son travail), même pour 10 concaténations de la dégradation des performances est pertinente; pour des milliers de concaténations, la différence est énorme.
Enseignements tirés de cette expérience rapide (facilement reproductible avec le code ci-dessus): ne jamais utiliser le
+=
pour concaténer des chaînes, même très basique cas où quelques enchaînements sont nécessaires (comme l'a dit, la création de nouvelles chaînes est cher de toute façon et met la pression sur le GC).Apache Commons-Lang a une ToStringBuilder classe qui est super facile à utiliser. Il fait un excellent travail à la fois de la manipulation de l'ajout de la logique ainsi que la mise en forme de la façon dont vous voulez que votre toString pour look.
Sera de retour de sortie qui ressemble à
com.blah.YourClass@abc1321f[a=whatever, b=foo]
.Ou sous une forme plus condensée en utilisant le chaînage:
Ou si vous souhaitez utiliser la réflexion pour inclure tous les champs de la classe:
Vous pouvez également personnaliser le style de la ToString si vous le souhaitez.
Voir l'exemple ci-dessous:
De sortie:
Que la longueur de la chaîne augmente, la concaténation de temps.
C'est là que le
StringBuilder
est certainement nécessaire.Comme vous le voyez, la concaténation:
UUID.randomUUID()+"---"
, n'affecte pas vraiment le temps.P. S.: je ne pense pas que Quand utiliser StringBuilder en Java est vraiment un duplicata de celui-ci.
Cette question parle
toString()
qui la plupart du temps ne permet pas de réaliser des enchaînements d'énormes chaînes.2019 mise à Jour
Depuis
java8
fois, les choses ont un peu changé. Il semble que maintenant(java13), la concaténation temps de+=
est pratiquement le même questr.concat()
. CependantStringBuilder
concaténation temps est toujours constant. (Original post ci-dessus a été légèrement modifié pour ajouter plus de commentaires de sortie)Intéressant de noter également
bytecode:8/java.version:13
combinaison a une bonne performance d'avantages par rapport àbytecode:8/java.version:8
java13
qui sont différentes. Mais je pense que la principale conclusion reste la même.Faire la méthode toString aussi lisible que possible!
La seule exception à cela dans mon livre, si vous pouvez prouver à moi qu'il consomme une quantité importante de ressources 🙂 (Oui, cela signifie que le profilage)
Également noter que le Java 5 compilateur génère plus rapide que le code manuscrite "StringBuffer" approche utilisée dans les versions antérieures de Java. Si vous utilisez "+" et les futures améliorations de est gratuit.
Il semble y avoir un débat à savoir si l'utilisation StringBuilder est toujours nécessaire avec les compilateurs gcc. J'ai donc pensé que je vais vous donner mon 2 cents de l'expérience.
J'ai un
JDBC
ensemble de résultats de 10k enregistrements (oui, j'ai besoin de tous en un seul lot.) À l'aide de l'opérateur + prend environ 5 minutes sur ma machine avecJava 1.8
. À l'aide destringBuilder.append("")
prend moins d'une seconde pour la même requête.Donc, la différence est énorme. L'intérieur d'une boucle
StringBuilder
est beaucoup plus rapide.Performance sage la concaténation de Chaîne à l'aide de '+' est plus cher parce qu'il a à faire à une toute nouvelle copie de la Chaîne depuis les Chaînes sont immuables en java. Ceci joue un rôle particulier si l'enchaînement est très fréquent, par exemple: à l'intérieur d'une boucle.
Voici ce que mon IDÉE suggère quand je tente de faire une telle chose:
Règles Générales:
Ici est un nice Jon Skeet blog autour de ce sujet.
Puis-je préciser que si vous allez effectuer une itération sur une collection et utiliser StringBuilder, vous voudrez peut-être vérifier Apache Commons Lang et StringUtils.join() (en différentes saveurs) ?
Indépendamment de la performance, il va vous éviter d'avoir à créer StringBuilders et pour les boucles pour ce qui semble être la millionième temps.
Voici ce que j'ai vérifié dans Java8
Utiliser StringBuilder
Je pense que nous devrions aller avec StringBuilder ajouter approche.
Raison d'être :
La Chaîne de concaténer va créer un nouvel objet string à chaque fois (Que String est immuable de l'objet) , de sorte qu'il permettra de créer de 3 objets.
Avec le générateur de Chaîne à un seul objet sera créé[StringBuilder est mutable] et la poursuite de la chaîne est ajouté à la fin.
Pour de simples chaînes de caractères comme que je préfère utiliser
Dans l'ordre, je dirais que la méthode préférée de la construction d'une chaîne à l'aide de StringBuilder, String#concat(), puis la surcharge opérateur+. StringBuilder est une augmentation significative des performances lorsque vous travaillez grandes chaînes tout comme l'utilisation de l'opérateur + est une diminution importante des performances (de façon exponentielle diminution importante que la taille de la Chaîne augmente). Le seul problème avec l'aide de .concat() est qu'il peut jeter NullPointerExceptions.