Concaténation de la chaîne: concat () vs "+" opérateur

en supposant les chaînes a et b:

a += b
a = a.concat(b)

sous le capot, c'est la même chose?

ici concat décomposé comme référence. J'aimerais pouvoir décomposer l'opérateur + pour voir ce que ça fait.

public String concat(String s) {

    int i = s.length();
    if (i == 0) {
        return this;
    }
    else {
        char ac[] = new char[count + i];
        getChars(0, count, ac, 0);
        s.getChars(0, i, ac, count);
        return new String(0, count + i, ac);
    }
}
417
demandé sur Peter Mortensen 2008-09-06 20:08:54

11 réponses

non, pas tout à fait.

Premièrement, il y a une légère différence dans la sémantique. Si a est null , alors a.concat(b) lance un NullPointerException mais a+=b traitera la valeur originale de a comme si elle était null . De plus, la méthode concat() n'accepte que les valeurs String tandis que l'opérateur + convertit silencieusement l'argument en chaîne (en utilisant la méthode toString() pour les objets). De sorte que le concat() la méthode est plus stricte dans ce qu'elle accepte.

pour regarder sous le capot, écrivez une classe simple avec a += b;

public class Concat {
    String cat(String a, String b) {
        a += b;
        return a;
    }
}

désassemble maintenant avec javap -c (inclus dans le Sun JDK). Vous devriez voir une liste comprenant:

java.lang.String cat(java.lang.String, java.lang.String);
  Code:
   0:   new     #2; //class java/lang/StringBuilder
   3:   dup
   4:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   7:   aload_1
   8:   invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11:  aload_2
   12:  invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15:  invokevirtual   #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/    String;
   18:  astore_1
   19:  aload_1
   20:  areturn

, a += b est l'équivalent de

a = new StringBuilder()
    .append(a)
    .append(b)
    .toString();

la méthode concat devrait être plus rapide. Cependant, avec plus de cordes La méthode StringBuilder gagne, du moins en termes de performance.

le code source de String et StringBuilder (et son package-private base class) est disponible en src.zip of the Sun JDK. Vous pouvez voir que vous construisez un tableau de char (redimensionnement si nécessaire) et puis le jeter lorsque vous créez la finale String . En pratique, l'allocation de la mémoire est étonnamment rapide.

Mise À Jour: Comme Pawel Adamski notes, la performance a changé dans les dernières HotSpot. javac produit toujours exactement le même code, mais le compilateur de bytecode triche. Le simple test échoue complètement parce que tout le corps du code est jeté. La somme System.identityHashCode (et non String.hashCode ) montre que le code StringBuffer présente un léger avantage. Sujet à changement lors de la prochaine mise à jour, ou si vous utilisez une JVM différente. De @lukaseder , une liste de JVM HotSpot intrinsèques .

489
répondu Tom Hawtin - tackline 2017-10-12 12:57:00

Niyaz est correct, mais il est également intéressant de noter que l'opérateur special + peut être converti en quelque chose de plus efficace par le compilateur Java. Java a une classe StringBuilder qui représente une chaîne mutable non-thread-safe. Lors de l'exécution d'un tas de concaténations de chaîne, le compilateur Java convertit silencieusement

String a = b + c + d;

en

String a = new StringBuilder(b).append(c).append(d).toString();

qui est nettement plus efficace pour les grandes cordes. Autant que je sache, cela ne se produit pas lorsque vous utilisez la méthode concat.

cependant, la méthode concat est plus efficace lorsqu'on concaténate une chaîne vide sur une chaîne existante. Dans ce cas, la JVM n'a pas besoin de créer un nouvel objet String et peut simplement retourner l'objet existant. Voir la documentation concat pour le confirmer.

donc si vous êtes super-préoccupé par l'efficacité, alors vous devriez utiliser la méthode concat en concaténant éventuellement-chaînes vides, et utiliser + autrement. Cependant, la différence de performance devrait être négligeable et vous ne devriez probablement jamais vous inquiéter à ce sujet.

82
répondu Eli Courtwright 2017-05-23 12:26:31

j'ai fait un test similaire à @marcio mais avec la boucle suivante à la place:

String c = a;
for (long i = 0; i < 100000L; i++) {
    c = c.concat(b); // make sure javac cannot skip the loop
    // using c += b for the alternative
}

juste pour faire bonne mesure, j'ai ajouté StringBuilder.append() . Chaque essai a été effectué 10 fois, avec 100k Rép.pour chaque essai. Voici les résultats:

  • StringBuilder gagne haut la main. Le résultat de temps d'horloge était 0 pour la plupart des descentes, et le plus long a pris 16M.
  • a += b prend environ 40000ms (40s) pour chaque course.
  • concat ne nécessite que 10000ms (10s) par course.

Je n'ai pas encore décomposé la classe pour voir les internes ou le lancer à travers profiler encore, mais je soupçonne a += b passe beaucoup de temps à créer de nouveaux objets de StringBuilder et puis les convertir en String .

42
répondu ckpwong 2008-09-06 19:25:12

Tom a raison de décrire exactement ce que fait l'opérateur+. Il crée un StringBuilder temporaire, Ajoute les pièces, et se termine avec toString() .

cependant, toutes les réponses jusqu'à présent ignorent les effets des optimisations HotSpot runtime. Plus précisément, ces opérations temporaires sont reconnues comme étant un modèle commun et sont remplacées par du code machine plus efficace à l'exécution.

@marcio: vous avez créé un micro-benchmark ; avec les JVM modernes, ce n'est pas un moyen valable de code de profil.

la raison pour laquelle l'optimisation du temps d'exécution est importante est que beaucoup de ces différences dans le code -- même en incluant la création d'objet -- sont complètement différentes une fois que le HotSpot est activé. Le seul moyen d'en être sûr est d'établir le profil de votre code in situ .

Enfin, toutes ces méthodes sont en fait incroyablement rapide. Cela pourrait être un cas de naissance prématurée optimisation. Si vous avez du code qui concaténate beaucoup les chaînes, la façon d'obtenir la vitesse maximale n'a probablement rien à voir avec les opérateurs que vous choisissez et à la place l'algorithme que vous utilisez!

22
répondu Jason Cohen 2008-09-06 19:19:52

Que Diriez-vous de quelques tests simples? Utilisé le code ci-dessous:

long start = System.currentTimeMillis();

String a = "a";

String b = "b";

for (int i = 0; i < 10000000; i++) { //ten million times
     String c = a.concat(b);
}

long end = System.currentTimeMillis();

System.out.println(end - start);
  • la version "a + b" exécutée en 2500ms .
  • Le a.concat(b) exécuté 1200ms .

testé plusieurs fois. L'exécution de la version concat() a pris la moitié du temps en moyenne.

Ce résultat m'a surpris parce que le concat() méthode crée toujours une nouvelle chaîne (elle renvoie un " new String(result) ". Il est bien connu que:

String a = new String("a") // more than 20 times slower than String a = "a"

pourquoi le compilateur n'a-t-il pas été capable d'optimiser la création de chaînes en code "a + b", sachant qu'il en résultait toujours la même chaîne? Il pourrait éviter une nouvelle création de chaîne. Si vous ne croyez pas la déclaration ci-dessus, tester pour votre auto.

19
répondu Marcio Aguiar 2013-09-10 20:55:32

la plupart des réponses ici sont de 2008. On dirait que les choses ont changé au fil du temps. Mes derniers benchmarks réalisés avec JMH montrent que sur Java 8 + est environ deux fois plus rapide que concat .

mon benchmark:

@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
public class StringConcatenation {

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State2 {
        public String a = "abc";
        public String b = "xyz";
    }

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State3 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
    }


    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State4 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
        public String d = "!@#";
    }

    @Benchmark
    public void plus_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b);
    }

    @Benchmark
    public void plus_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c);
    }

    @Benchmark
    public void plus_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c+state.d);
    }

    @Benchmark
    public void stringbuilder_2(State2 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).toString());
    }

    @Benchmark
    public void stringbuilder_3(State3 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).toString());
    }

    @Benchmark
    public void stringbuilder_4(State4 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).append(state.d).toString());
    }

    @Benchmark
    public void concat_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b));
    }

    @Benchmark
    public void concat_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c)));
    }


    @Benchmark
    public void concat_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c.concat(state.d))));
    }
}

Résultats:

Benchmark                             Mode  Cnt         Score         Error  Units
StringConcatenation.concat_2         thrpt   50  24908871.258 ± 1011269.986  ops/s
StringConcatenation.concat_3         thrpt   50  14228193.918 ±  466892.616  ops/s
StringConcatenation.concat_4         thrpt   50   9845069.776 ±  350532.591  ops/s
StringConcatenation.plus_2           thrpt   50  38999662.292 ± 8107397.316  ops/s
StringConcatenation.plus_3           thrpt   50  34985722.222 ± 5442660.250  ops/s
StringConcatenation.plus_4           thrpt   50  31910376.337 ± 2861001.162  ops/s
StringConcatenation.stringbuilder_2  thrpt   50  40472888.230 ± 9011210.632  ops/s
StringConcatenation.stringbuilder_3  thrpt   50  33902151.616 ± 5449026.680  ops/s
StringConcatenation.stringbuilder_4  thrpt   50  29220479.267 ± 3435315.681  ops/s
16
répondu Paweł Adamski 2017-09-29 08:56:14

fondamentalement, il y a deux différences importantes entre + et la méthode concat .

  1. si vous utilisez la méthode concat alors vous ne pourrez concaténer que des chaînes alors que dans le cas de l'opérateur + , vous pouvez également concaténer la chaîne avec n'importe quel type de données.

    Par Exemple:

    String s = 10 + "Hello";
    

    In dans ce cas, la sortie doit être 10Hello .

    String s = "I";
    String s1 = s.concat("am").concat("good").concat("boy");
    System.out.println(s1);
    

    Dans le cas ci-dessus, vous devez fournir deux chaînes obligatoires.

  2. la deuxième et principale différence entre + et concat est la suivante:

    Cas 1: Supposons que je concatte les mêmes chaînes avec l'opérateur concat dans ce façon

    String s="I";
    String s1=s.concat("am").concat("good").concat("boy");
    System.out.println(s1);
    

    dans ce cas le nombre total d'objets créés dans le pool est de 7 comme ceci:

    I
    am
    good
    boy
    Iam
    Iamgood
    Iamgoodboy
    

    Cas 2:

    maintenant je vais concatiner les mêmes cordes via + opérateur

    String s="I"+"am"+"good"+"boy";
    System.out.println(s);
    

    dans le cas ci-dessus, le nombre total d'objets créés n'est que de 5.

    en fait quand nous concaténons les cordes via + opérateur puis il maintient une classe de StringBuffer pour effectuer la même tâche comme suit: -

    StringBuffer sb = new StringBuffer("I");
    sb.append("am");
    sb.append("good");
    sb.append("boy");
    System.out.println(sb);
    

    de cette façon, il ne créera que cinq objets.

donc les gars ce sont les différences de base entre + et la concat méthode. Enjoy:)

4
répondu Deepak Sharma 2017-06-05 21:15:53

par souci d'exhaustivité, je voulais ajouter que la définition de l'opérateur " + "se trouve dans le JLS SE8 15.18.1 :

si une seule expression opérande est de type String, alors string la conversion (§5.1.11) est effectuée sur l'autre opérande pour produire une chaîne au moment de l'exécution.

le résultat de la concaténation d'une chaîne de caractères est une référence à un objet String qui est la concaténation des deux opérande cordes. Caractère de gauche opérande précéder les caractères de la droite opérande dans la chaîne nouvellement créée.

L'objet String est nouvellement créé (§12.5) sauf si l'expression est expression constante (§15.28).

à propos de la mise en œuvre le JLS dit ce qui suit:

une implémentation peut choisir d'effectuer la conversion et la concaténation dans une étape pour éviter de créer puis de jeter un intermédiaire Objet de type String. Pour augmenter la performance de la chaîne répétée concaténation, un compilateur Java peut utiliser la classe StringBuffer ou technique similaire pour réduire le nombre d'objets de chaîne intermédiaire qui sont créés par l'évaluation d'une expression.

pour les types primitifs, une implémentation peut également optimiser création d'un objet wrapper en convertissant directement à partir d'une primitive type de chaîne.

ainsi, à en juger par "un compilateur Java peut utiliser la classe StringBuffer ou une technique similaire pour réduire", différents compilateurs pourraient produire différents byte-code.

2
répondu dingalapadum 2017-03-26 15:00:44

le + l'opérateur peut fonctionner entre une chaîne et une valeur de type de données chaîne, char, Entier, Double ou flottant. Il convertit juste la valeur à sa représentation de chaîne de caractères avant la concaténation.

le concat operator ne peut être fait que sur et avec des cordes. Il vérifie la compatibilité des types de données et envoie une erreur, s'ils ne correspondent pas.

sauf que le code que vous avez fourni fait la même chose.

2
répondu Niyaz 2017-04-01 18:03:27

Je ne pense pas.

a.concat(b) est implémenté en String et je pense que l'implémentation n'a pas beaucoup changé depuis les premières machines java. L'implémentation de l'opération + dépend de la version Java et du compilateur. Actuellement + est mis en œuvre en utilisant StringBuffer pour rendre l'opération aussi rapide que possible. Peut-être que dans l'avenir, cela va changer. Dans les versions précédentes de java + l'opération sur les chaînes était beaucoup plus lent qu'il produit des résultats intermédiaires.

je suppose que += est implémenté en utilisant + et optimisé de la même manière.

2
répondu Bartosz Bierkowski 2018-07-27 14:46:12

Lorsqu'on utilise +, la vitesse diminue à mesure que la longueur de la chaîne augmente, mais lorsqu'on utilise concat, la vitesse est plus stable, et la meilleure option est d'utiliser la classe StringBuilder qui a une vitesse stable pour le faire.

je suppose que vous pouvez comprendre pourquoi. Mais la meilleure façon de créer de longues chaînes est d'utiliser StringBuilder () et append (), chaque vitesse sera inacceptable.

0
répondu iamreza 2017-06-05 21:18:03