StringBuilder vs concaténation de chaînes dans toString () en Java

926

Compte tenu des 2 toString()implémentations ci-dessous, laquelle 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 encore, étant donné que nous n'avons que 3 propriétés, cela pourrait ne pas faire de différence, mais à quel moment basculeriez-vous de +concat à StringBuilder?

non séquenceur
la source
40
À quel moment passez-vous à StringBuilder? Quand cela affecte la mémoire ou les performances. Ou quand ça pourrait. Si vous ne faites cela qu'une seule fois pour quelques cordes, pas de soucis. Mais si vous allez le faire encore et encore, vous devriez voir une différence mesurable lors de l'utilisation de StringBuilder.
ewall
quelle est la moyenne de 100 en paramètre?
Asif Mushtaq
2
@UnKnown 100 est la taille initiale de StringBuilder
non séquenceur
@nonsequitor Le nombre maximum de caractères sera donc de 100?
Asif Mushtaq
10
@Unknown non seulement la taille initiale, si vous connaissez la taille approximative de la chaîne avec laquelle vous traitez, vous pouvez dire StringBuildercombien de taille allouer à l'avance sinon, si elle manque d'espace, elle devra doubler la taille en créant un nouveau char[]tableau puis copiez les données - ce qui est coûteux. Vous pouvez tricher en donnant la taille, puis il n'est pas nécessaire de créer ce tableau - donc si vous pensez que votre chaîne aura environ 100 caractères, vous pouvez définir le StringBuilder à cette taille et il n'aura jamais à se développer en interne.
non séquenceur

Réponses:

964

La version 1 est préférable car elle est plus courte et le compilateur la transformera en fait en version 2 - aucune différence de performance que ce soit.

Plus important encore, étant donné que nous n'avons que 3 propriétés, cela pourrait ne pas faire de différence, mais à quel moment passez-vous de concat à constructeur?

Au point où vous concaténez dans une boucle - c'est généralement lorsque le compilateur ne peut pas se substituer StringBuilderpar lui-même.

Michael Borgwardt
la source
19
C'est vrai, mais la référence au langage indique également que cela est facultatif. En fait, je viens de faire un test simple avec JRE 1.6.0_15 et je n'ai vu aucune optimisation du compilateur dans la classe décompilée.
bruno conde
37
J'ai juste essayé le code de la question (compilé sur JDK 1.6.0_16) et trouvé l'optimisation comme prévu. Je suis sûr que tous les compilateurs modernes le feront.
Michael Borgwardt
22
Vous avez raison. En regardant le bytecode, je peux clairement voir l'optimisation de StringBuilder. J'utilisais un décompilateur et, d'une certaine manière, il se reconvertit en concaténation. +1
bruno conde
80
Ne pas battre un cheval mort, mais le libellé de la spécification est: 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é peut être . Étant donné que cela est officiellement facultatif (bien que très probablement mis en œuvre), ne devrions-nous pas nous protéger?
Lucas
93
@Lucas: Non, nous ne devrions pas. Si le compilateur décide de ne pas effectuer cette optimisation, ce sera parce que cela n'en vaut pas la peine. Dans 99% des cas, le compilateur sait mieux quelle optimisation en vaut la peine, donc en règle générale, le développeur ne doit pas interférer. Bien sûr, votre situation peut tomber dans les 1% restants, mais cela ne peut être vérifié que par une analyse comparative (prudente).
sleske
255

La clé est de savoir si vous écrivez une seule concaténation en un seul endroit ou si vous l'accumulez au fil du temps.

Pour l'exemple que vous avez donné, il est inutile d'utiliser explicitement StringBuilder. (Regardez le code compilé pour votre premier cas.)

Mais si vous construisez une chaîne, par exemple à l'intérieur d'une boucle, utilisez StringBuilder.

Pour clarifier, en supposant que énormeArray contient des milliers de chaînes, code comme ceci:

...
String result = "";
for (String s : hugeArray) {
    result = result + s;
}

est une perte de temps et de mémoire par rapport à:

...
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
    sb.append(s);
}
String result = sb.toString();
joel.neely
la source
3
Oui, StringBuilder n'a pas besoin de recréer l'objet String encore et encore.
Olga
124
Bon sang, j'ai utilisé ces 2 fonctions pour tester une grosse chaîne dans laquelle je travaille. 6.51min vs 11secs
user1722791
1
Au fait, vous pouvez également l'utiliser result += s;(dans le premier exemple)
RAnders00
1
combien d'objets seront créés par cette instruction? "{a:"+ a + ", b:" + b + ", c: " + c +"}";
Asif Mushtaq
1
Qu'en est-il de quelque chose comme: String str = (a == null)? null: a '+ (b == null)? null: b '+ (c == null)? c: c '+ ...; ? Cela empêchera-t-il l'optimisation de se produire?
amitfr
75

Je préfère:

String.format( "{a: %s, b: %s, c: %s}", a, b, c );

... car il est court et lisible.

Je n'optimiserais pas cela pour la vitesse à moins que vous ne l'utilisiez dans une boucle avec un nombre de répétitions très élevé et que vous ayez mesuré la différence de performance.

Je suis d'accord, si vous devez sortir beaucoup de paramètres, ce formulaire peut devenir déroutant (comme le dit l'un des commentaires). Dans ce cas, je passerais à une forme plus lisible (peut-être en utilisant ToStringBuilder d'apache-commons - tiré de la réponse de matt b) et ignorerais à nouveau les performances.

tangens
la source
65
Il est en fait plus long, contient plus de symboles et a des variables et un texte hors séquence.
Tom Hawtin - tackline
4
Alors, diriez-vous qu'il est moins lisible que l'un des autres approches?
tangens
3
Je préfère écrire ceci, car il est plus facile d'ajouter plus de variables, mais je ne suis pas sûr que ce soit plus lisible - d'autant plus que le nombre d'arguments devient important. Cela ne fonctionne pas non plus lorsque vous devez ajouter des bits à des moments différents.
Alex Feinman
78
Semble plus difficile à lire (pour moi). Maintenant, je dois aller et venir entre {...} et les paramètres.
Steve Kuo
10
Je préfère cette forme à, car elle est sûre si l'un des paramètres estnull
rds
74

Dans la plupart des cas, vous ne verrez pas de différence réelle entre les deux approches, mais il est facile de construire un scénario pire comme celui-ci:

public class Main
{
    public static void main(String[] args)
    {
        long now = System.currentTimeMillis();
        slow();
        System.out.println("slow elapsed " + (System.currentTimeMillis() - now) + " ms");

        now = System.currentTimeMillis();
        fast();
        System.out.println("fast elapsed " + (System.currentTimeMillis() - now) + " ms");
    }

    private static void fast()
    {
        StringBuilder s = new StringBuilder();
        for(int i=0;i<100000;i++)
            s.append("*");      
    }

    private static void slow()
    {
        String s = "";
        for(int i=0;i<100000;i++)
            s+="*";
    }
}

La sortie est:

slow elapsed 11741 ms
fast elapsed 7 ms

Le problème est que + = ajouter à une chaîne reconstruit une nouvelle chaîne, donc cela coûte quelque chose de linéaire à la longueur de vos chaînes (somme des deux).

Donc - à votre question:

La deuxième approche serait plus rapide, mais elle est moins lisible et plus difficile à maintenir. Comme je l'ai dit, dans votre cas particulier, vous ne verriez probablement pas la différence.

Omry Yadan
la source
N'oubliez pas .concat (). Je suppose que le temps écoulé doit être compris entre 10 et 18 ms, ce qui le rend négligeable lors de l'utilisation de chaînes courtes comme l'exemple de publication d'origine.
Droo
9
Pendant que vous avez raison +=, l'exemple d'origine était une séquence de +, que le compilateur transforme en un seul string.concatappel. Vos résultats ne s'appliquent pas.
Blindy
1
@Blindy & Droo: - Vous avez tous les deux raison.Utiliser .concate dans un tel scénario est la meilleure solution, car + = crée un nouvel objet à chaque exécution de la routine de boucle de temps.
perilbrain
3
savez-vous que son toString () n'est pas appelé dans une boucle?
Omry Yadan
J'ai essayé cet exemple pour tester la vitesse. Mes résultats sont donc: 29672 ms écoulées lentement; écoulé rapidement 15 ms. La réponse est donc évidente. Mais si ce serait 100 itérations - le temps est le même - 0 ms. Si 500 itérations - 16 ms et 0 ms. Etc.
Ernestas Gruodis
28

J'ai également eu un conflit avec mon patron sur le fait d'utiliser Append ou + .Comme ils utilisent Append (je n'arrive toujours pas à comprendre comme on dit à chaque fois qu'un nouvel objet est créé). J'ai donc pensé faire de la R & D.Bien que j'aime l'explication de Michael Borgwardt, mais je voulais juste montrer une explication si quelqu'un aurait vraiment besoin de savoir à l'avenir.

/**
 *
 * @author Perilbrain
 */
public class Appc {
    public Appc() {
        String x = "no name";
        x += "I have Added a name" + "We May need few more names" + Appc.this;
        x.concat(x);
        // x+=x.toString(); --It creates new StringBuilder object before concatenation so avoid if possible
        //System.out.println(x);
    }

    public void Sb() {
        StringBuilder sbb = new StringBuilder("no name");
        sbb.append("I have Added a name");
        sbb.append("We May need few more names");
        sbb.append(Appc.this);
        sbb.append(sbb.toString());
        // System.out.println(sbb.toString());
    }
}

et le démontage de la classe ci-dessus se présente comme

 .method public <init>()V //public Appc()
  .limit stack 2
  .limit locals 2
met001_begin:                                  ; DATA XREF: met001_slot000i
  .line 12
    aload_0 ; met001_slot000
    invokespecial java/lang/Object.<init>()V
  .line 13
    ldc "no name"
    astore_1 ; met001_slot001
  .line 14

met001_7:                                      ; DATA XREF: met001_slot001i
    new java/lang/StringBuilder //1st object of SB
    dup
    invokespecial java/lang/StringBuilder.<init>()V
    aload_1 ; met001_slot001
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    ldc "I have Added a nameWe May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    aload_0 ; met001_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    astore_1 ; met001_slot001
  .line 15
    aload_1 ; met001_slot001
    aload_1 ; met001_slot001
    invokevirtual java/lang/String.concat(Ljava/lang/String;)Ljava/lang/Strin\
g;
    pop
  .line 18
    return //no more SB created
met001_end:                                    ; DATA XREF: met001_slot000i ...

; ===========================================================================

;met001_slot000                                ; DATA XREF: <init>r ...
    .var 0 is this LAppc; from met001_begin to met001_end
;met001_slot001                                ; DATA XREF: <init>+6w ...
    .var 1 is x Ljava/lang/String; from met001_7 to met001_end
  .end method
;44-1=44
; ---------------------------------------------------------------------------


; Segment type: Pure code
  .method public Sb()V //public void Sb
  .limit stack 3
  .limit locals 2
met002_begin:                                  ; DATA XREF: met002_slot000i
  .line 21
    new java/lang/StringBuilder
    dup
    ldc "no name"
    invokespecial java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    astore_1 ; met002_slot001
  .line 22

met002_10:                                     ; DATA XREF: met002_slot001i
    aload_1 ; met002_slot001
    ldc "I have Added a name"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 23
    aload_1 ; met002_slot001
    ldc "We May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 24
    aload_1 ; met002_slot001
    aload_0 ; met002_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    pop
  .line 25
    aload_1 ; met002_slot001
    aload_1 ; met002_slot001
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 28
    return
met002_end:                                    ; DATA XREF: met002_slot000i ...


;met002_slot000                                ; DATA XREF: Sb+25r
    .var 0 is this LAppc; from met002_begin to met002_end
;met002_slot001                                ; DATA XREF: Sb+9w ...
    .var 1 is sbb Ljava/lang/StringBuilder; from met002_10 to met002_end
  .end method
;96-49=48
; ---------------------------------------------------------------------------

À partir des deux codes ci-dessus, vous pouvez voir que Michael a raison. Dans chaque cas, un seul objet SB est créé.

péril cerveau
la source
27

Depuis Java 1.5, la concaténation simple d'une ligne avec "+" et StringBuilder.append () génère exactement le même bytecode.

Donc, pour des raisons de lisibilité du code, utilisez "+".

2 exceptions:

  • environnement multithread: StringBuffer
  • concaténation en boucles: StringBuilder / StringBuffer
Zofren
la source
22

En utilisant la dernière version de Java (1.8), le démontage ( javap -c) montre l'optimisation introduite par le compilateur. +ainsi sb.append()va générer un code très similaire. Cependant, il vaudra la peine d'examiner le comportement si nous l'utilisons +dans une boucle for.

Ajouter des chaînes en utilisant + dans une boucle for

Java:

public String myCatPlus(String[] vals) {
    String result = "";
    for (String val : vals) {
        result = result + val;
    }
    return result;
}

ByteCode :( forextrait de boucle)

12: iload         5
14: iload         4
16: if_icmpge     51
19: aload_3
20: iload         5
22: aaload
23: astore        6
25: new           #3                  // class java/lang/StringBuilder
28: dup
29: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
32: aload_2
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: aload         6
38: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
44: astore_2
45: iinc          5, 1
48: goto          12

Ajout de chaînes à l'aide de stringbuilder.append

Java:

public String myCatSb(String[] vals) {
    StringBuilder sb = new StringBuilder();
    for(String val : vals) {
        sb.append(val);
    }
    return sb.toString();
}

ByteCdoe :( forextrait de boucle)

17: iload         5
19: iload         4
21: if_icmpge     43
24: aload_3
25: iload         5
27: aaload
28: astore        6
30: aload_2
31: aload         6
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: pop
37: iinc          5, 1
40: goto          17
43: aload_2

Il y a cependant une différence flagrante . Dans le premier cas, où a +été utilisé, nouveau StringBuilderest créé pour chaque pour l'itération de boucle et le résultat généré est stocké en faisant un toString()appel (29 à 41). Vous générez donc des chaînes intermédiaires dont vous n'avez vraiment pas besoin lorsque vous utilisez l' +opérateur en forboucle.

porteur de l'anneau
la source
1
S'agit-il d'Oracle JDK ou d'OpenJDK?
Christophe Roussy
12

Dans Java 9, la version 1 devrait être plus rapide car elle est convertie en invokedynamicappel. Plus de détails peuvent être trouvés dans JEP-280 :

L'idée est de remplacer la danse de l'appendice StringBuilder entière par un simple appel dynamique invoqué à java.lang.invoke.StringConcatFactory, qui acceptera les valeurs nécessitant une concaténation.

ZhekaKozlov
la source
9

Pour des raisons de performances, l'utilisation de +=( Stringconcaténation) est déconseillée. La raison en est: Java Stringest immuable, chaque fois qu'une nouvelle concaténation est effectuée, une nouvelle Stringest créée (la nouvelle a une empreinte digitale différente de l'ancienne déjà dans le pool de chaînes) ). La création de nouvelles chaînes exerce une pression sur le GC et ralentit le programme: la création d'objets coûte cher.

Le code ci-dessous devrait le rendre plus pratique et plus clair à la fois.

public static void main(String[] args) 
{
    // warming up
    for(int i = 0; i < 100; i++)
        RandomStringUtils.randomAlphanumeric(1024);
    final StringBuilder appender = new StringBuilder();
    for(int i = 0; i < 100; i++)
        appender.append(RandomStringUtils.randomAlphanumeric(i));

    // testing
    for(int i = 1; i <= 10000; i*=10)
        test(i);
}

public static void test(final int howMany) 
{
    List<String> samples = new ArrayList<>(howMany);
    for(int i = 0; i < howMany; i++)
        samples.add(RandomStringUtils.randomAlphabetic(128));

    final StringBuilder builder = new StringBuilder();
    long start = System.nanoTime();
    for(String sample: samples)
        builder.append(sample);
    builder.toString();
    long elapsed = System.nanoTime() - start;
    System.out.printf("builder - %d - elapsed: %dus\n", howMany, elapsed / 1000);

    String accumulator = "";
    start = System.nanoTime();
    for(String sample: samples)
        accumulator += sample;
    elapsed = System.nanoTime() - start;
    System.out.printf("concatenation - %d - elapsed: %dus\n", howMany, elapsed / (int) 1e3);

    start = System.nanoTime();
    String newOne = null;
    for(String sample: samples)
        newOne = new String(sample);
    elapsed = System.nanoTime() - start;
    System.out.printf("creation - %d - elapsed: %dus\n\n", howMany, elapsed / 1000);
}

Les résultats d'une analyse sont présentés ci-dessous.

builder - 1 - elapsed: 132us
concatenation - 1 - elapsed: 4us
creation - 1 - elapsed: 5us

builder - 10 - elapsed: 9us
concatenation - 10 - elapsed: 26us
creation - 10 - elapsed: 5us

builder - 100 - elapsed: 77us
concatenation - 100 - elapsed: 1669us
creation - 100 - elapsed: 43us

builder - 1000 - elapsed: 511us
concatenation - 1000 - elapsed: 111504us
creation - 1000 - elapsed: 282us

builder - 10000 - elapsed: 3364us 
concatenation - 10000 - elapsed: 5709793us
creation - 10000 - elapsed: 972us

Ne tenant pas compte des résultats pour 1 concaténation (JIT ne faisait pas encore son travail), même pour 10 concaténations, la pénalité de performance est pertinente; pour des milliers de concaténations, la différence est énorme.

Leçons tirées de cette expérience très rapide (facilement reproductible avec le code ci-dessus): ne jamais utiliser le +=pour concaténer des chaînes ensemble, même dans des cas très basiques où quelques concaténations sont nécessaires (comme dit, la création de nouvelles chaînes est de toute façon coûteuse et met la pression sur le GC).

Paolo Maresca
la source
9

Voir l'exemple ci-dessous:

static final int MAX_ITERATIONS = 50000;
static final int CALC_AVG_EVERY = 10000;

public static void main(String[] args) {
    printBytecodeVersion();
    printJavaVersion();
    case1();//str.concat
    case2();//+=
    case3();//StringBuilder
}

static void case1() {
    System.out.println("[str1.concat(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str = str.concat(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case2() {
    System.out.println("[str1+=str2]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str += UUID.randomUUID() + "---";
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case3() {
    System.out.println("[str1.append(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    StringBuilder str = new StringBuilder("");
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str.append(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");

}

static void saveTime(List<Long> executionTimes, long startTime) {
    executionTimes.add(System.currentTimeMillis() - startTime);
    if (executionTimes.size() % CALC_AVG_EVERY == 0) {
        out.println("average time for " + executionTimes.size() + " concatenations: "
                + NumberFormat.getInstance().format(executionTimes.stream().mapToLong(Long::longValue).average().orElseGet(() -> 0))
                + " ms avg");
        executionTimes.clear();
    }
}

Production:

version du bytecode java: 8
java.version: 1.8.0_144
[str1.concat (str2)]
temps moyen pour 10000 concaténations: 0,096 ms
temps moyen pour 10000 concaténations: 0,185 ms
temps moyen pour 10000 concaténations: 0,327 ms
temps moyen pour 10000 concaténations: 0,501 ms de
temps moyen pour 10000 concaténations: 0,656 ms de moyenne
Chaîne de longueur créée: 1950000 en 17745 ms
[str1 + = str2]
temps moyen pour 10000 concaténations: 0,21 ms de
temps moyen pour 10000 concaténations: 0,652 ms
temps moyen pour 10000 concaténations: 1,129 ms temps moyen pour 10000 concaténations: 2,302 ms moy
temps moyen pour 10000 concaténations: 1,727 ms moy

Chaîne créée de longueur: 1950000 en 60279 ms
[str1.append (str2)]
temps moyen pour 10000 concaténations: 0,002 ms
temps moyen pour 10000 concaténations: 0,002 ms
temps moyen pour 10000 concaténations: 0,002 ms
temps moyen pour 10000 concaténations: 0,002 ms
temps moyen pour 10000 concaténations: 0,002 ms moyenne
Longueur de la chaîne créée: 1950000 en 100 ms

À mesure que la longueur de chaîne augmente, le temps de concaténation augmente également.
C'est là que le StringBuilderbesoin est certain.
Comme vous le voyez, la concaténation:UUID.randomUUID()+"---" n'affecte pas vraiment le temps.

PS: Je ne pense pas que quand utiliser StringBuilder en Java est vraiment un doublon de cela.
Cette question parle de la toString()plupart du temps qui n'effectue pas de concaténation d'énormes cordes.


Mise à jour 2019

Depuis des java8temps, les choses ont un peu changé. Il semble que maintenant (java13), le temps de concaténation de +=soit pratiquement le même que str.concat(). Cependant, le StringBuildertemps de concaténation est toujours constant . (Le message d'origine ci-dessus a été légèrement modifié pour ajouter une sortie plus détaillée)

java bytecode version: 13
java.version: 13.0.1
[str1.concat (str2)]
temps moyen pour 10000 concaténations: 0,047 ms
temps moyen pour 10000 concaténations: 0,1 ms
temps moyen pour 10000 concaténations: 0,17 ms
temps moyen pour 10000 concaténations: 0,255 ms
durée moyenne pour 10000 concaténations: 0,336 ms moyenne
Chaîne créée de longueur: 1950000 en 9147 ms
[str1 + = str2]
durée moyenne pour 10000 concaténations: 0,037 ms moyenne
durée moyenne pour 10000 concaténations: 0,097 ms moyenne
temps moyen pour 10000 concaténations: 0,249 ms temps moyen pour 10000 concaténations: 0,326 ms moy
temps moyen pour 10000 concaténations: 0,298 ms moyenne

Chaîne créée de longueur: 1950000 en 10191 ms
[str1.append (str2)]
temps moyen pour 10000 concaténations: 0,001 ms temps moyen en
moyenne pour 10000 concaténations: 0,001 ms temps moyen en
moyenne pour 10000 concaténations: 0,001 ms temps moyen en
moyenne pour 10000 concaténations: 0,001 ms
durée moyenne pour 10000 concaténations: 0,001 ms moyenne
Chaîne créée de longueur: 1950000 en 43 ms

Il convient de noter également que la bytecode:8/java.version:13combinaison présente un bon avantage en termes de performances par rapport àbytecode:8/java.version:8

Marinos An
la source
Cela devrait être la réponse acceptée .. cela dépend de la taille du String String qui détermine le choix de concat ou StringBuilder
user1428716
@ user1428716 FYI: réponse mise à jour avec des résultats java13qui sont maintenant différents. Mais je pense que la conclusion principale reste la même.
Marinos
7

Apache Commons-Lang a une classe ToStringBuilder qui est super facile à utiliser. Il fait un bon travail de gestion de la logique d'ajout ainsi que de formatage de l'apparence de votre toString.

public void toString() {
     ToStringBuilder tsb =  new ToStringBuilder(this);
     tsb.append("a", a);
     tsb.append("b", b)
     return tsb.toString();
}

Renvoie une sortie qui ressemble com.blah.YourClass@abc1321f[a=whatever, b=foo].

Ou sous une forme plus condensée en utilisant le chaînage:

public void toString() {
     return new ToStringBuilder(this).append("a", a).append("b", b").toString();
}

Ou si vous souhaitez utiliser la réflexion pour inclure tous les domaines de la classe:

public String toString() {
    return ToStringBuilder.reflectionToString(this);
}

Vous pouvez également personnaliser le style de la ToString si vous le souhaitez.

mat b
la source
4

Rendez la méthode toString aussi lisible que possible!

La seule exception pour cela dans mon livre est si vous pouvez me prouver qu'il consomme des ressources importantes :) (Oui, cela signifie un profilage)

Notez également que le compilateur Java 5 génère un code plus rapide que l'approche manuscrite "StringBuffer" utilisée dans les versions antérieures de Java. Si vous utilisez "+", ces améliorations et celles à venir sont gratuites.

Thorbjørn Ravn Andersen
la source
3

Il semble y avoir un débat sur l'utilisation de StringBuilder avec les compilateurs actuels. J'ai donc pensé donner mes 2 cents d'expérience.

J'ai un JDBCensemble de résultats de 10 000 enregistrements (oui, j'ai besoin de tous en un seul lot.) L'utilisation de l'opérateur + prend environ 5 minutes sur ma machine avec Java 1.8. L'utilisation stringBuilder.append("")prend moins d'une seconde pour la même requête.

La différence est donc énorme. À l'intérieur d'une boucle StringBuilderest beaucoup plus rapide.

Tourbillon
la source
2
Je pense que le débat porte sur son utilisation en dehors des boucles. Je pense qu'il y a un consensus que vous devez utiliser dans une boucle.
Jordanie
2

La concaténation de String en termes de performances en utilisant '+' est plus coûteuse car elle doit faire une toute nouvelle copie de String car les Strings sont immuables en Java. Cela joue un rôle particulier si la concaténation est très fréquente, par exemple: à l'intérieur d'une boucle. Voici ce que mon IDEA suggère lorsque j'essaie de faire une telle chose:

entrez la description de l'image ici

Règles générales:

  • Dans une seule affectation de chaîne, l'utilisation de la concaténation de chaîne est correcte.
  • Si vous effectuez une boucle pour créer un grand bloc de données de caractères, optez pour StringBuffer.
  • L'utilisation de + = sur une chaîne sera toujours moins efficace que l'utilisation d'un StringBuffer, donc cela devrait sonner des cloches d'avertissement - mais dans certains cas, l'optimisation obtenue sera négligeable par rapport aux problèmes de lisibilité, alors utilisez votre bon sens.

Voici un joli blog Jon Skeet sur ce sujet.

Sudip Bhandari
la source
2
Vous ne devez jamais utiliser StringBuffer sauf si vous avez absolument besoin d'un accès synchronisé à partir de plusieurs threads. Sinon, préférez StringBuilder qui n'est pas synchronisé et a donc moins de surcharge.
Erki der Loony
1

Puis-je souligner que si vous allez parcourir une collection et utiliser StringBuilder, vous voudrez peut-être consulter Apache Commons Lang et StringUtils.join () (dans différentes )?

Quelles que soient les performances, cela vous évitera de créer des StringBuilders et des boucles pour ce qui semble être la millionième fois.

Brian Agnew
la source
1

Voici ce que j'ai vérifié en Java8

  • Utilisation de la concaténation de chaînes
  • Utilisation de StringBuilder

    long time1 = System.currentTimeMillis();
    usingStringConcatenation(100000);
    System.out.println("usingStringConcatenation " + (System.currentTimeMillis() - time1) + " ms");
    
    time1 = System.currentTimeMillis();
    usingStringBuilder(100000);
    System.out.println("usingStringBuilder " + (System.currentTimeMillis() - time1) + " ms");
    
    
    private static void usingStringBuilder(int n)
    {
        StringBuilder str = new StringBuilder();
        for(int i=0;i<n;i++)
            str.append("myBigString");    
    }
    
    private static void usingStringConcatenation(int n)
    {
        String str = "";
        for(int i=0;i<n;i++)
            str+="myBigString";
    }

C'est vraiment un cauchemar si vous utilisez la concaténation de chaînes pour un grand nombre de chaînes.

usingStringConcatenation 29321 ms
usingStringBuilder 2 ms
nagendra547
la source
-1

Je pense que nous devrions aller avec l'approche append StringBuilder. Raison d'être :

  1. La concaténation de chaîne créera un nouvel objet chaîne à chaque fois (comme la chaîne est un objet immuable), elle créera donc 3 objets.

  2. Avec le générateur de chaînes, un seul objet sera créé [StringBuilder est mutable] et la chaîne supplémentaire y est ajoutée.

VishalTambe
la source
Pourquoi cette réponse est-elle rejetée? docs.oracle.com/javase/8/docs/api/java/util/stream/… - Réduction
Mutable
-4

Pour les cordes simples comme ça, je préfère utiliser

"string".concat("string").concat("string");

Dans l'ordre, je dirais que la méthode préférée de construction d'une chaîne utilise StringBuilder, String # concat (), puis l'opérateur surchargé +. StringBuilder est une augmentation significative des performances lorsque le travail de grandes chaînes, tout comme l'utilisation de l'opérateur +, est une diminution importante des performances (diminution exponentielle à mesure que la taille de la chaîne augmente). Le seul problème avec l'utilisation de .concat () est qu'il peut lever des NullPointerExceptions.

Droo
la source
9
L'utilisation de concat () est susceptible de donner de moins bons résultats que '+' car le JLS permet de convertir '+' en StringBuilder, et très probablement toutes les JVM le font ou utilisent une alternative plus efficace - la même chose n'est probablement pas vraie pour concat qui doit créer et supprimer au moins une chaîne intermédiaire complète dans votre exemple.
Lawrence Dol