555 votes

Concaténation de chaînes de caractères : concat() vs + opérateur

Je suis curieux et je n'étais pas sûr, alors j'ai pensé demander :

en supposant que les cordes a et b.

a+=b
a.concat(b)

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

Edit :

Voici concat décompilé comme référence, j'aimerais pouvoir décompiler l'opérateur + pour voir ce qu'il fait, mais je ne sais pas encore comment le faire.

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);
    }
}

622voto

Tom Hawtin - tackline Points 82671

Non, pas tout à fait.

Tout d'abord, il y a une légère différence de sémantique. Si a est nul, alors le concat() mais la première traitera la valeur originale de a comme si elle était "null". En outre, la méthode concat() n'accepte que la méthode String tandis que les + convertira silencieusement l'argument en String (en utilisant l'opérateur toString() pour les objets). Ainsi, la concat() 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;
    }
}

Maintenant désassemblez avec javap -c (inclus dans le Sun JDK). Vous devriez voir une liste incluant :

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

Donc, 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 chaînes, la méthode StringBuilder l'emporte, du moins en termes de performances.

Le code source de String et StringBuilder (et sa classe de base privée par paquetage) est disponible dans src.zip du Sun JDK. Vous pouvez voir que vous construisez un tableau de caractères (en le redimensionnant si nécessaire), puis que vous le jetez lorsque vous créez la chaîne finale. En pratique, l'allocation de mémoire est étonnamment rapide.

98voto

Eli Courtwright Points 53071

Niyaz est correcte, mais il convient également de noter que l'opérateur spécial + peut être converti en quelque chose de plus efficace par le compilateur Java. Java dispose d'une classe StringBuilder qui représente une chaîne de caractères mutable et non protégée contre les threads. Lorsqu'il effectue un grand nombre de concaténations de chaînes, le compilateur Java convertit silencieusement l'opérateur + en un opérateur plus efficace.

String a = b + c + d;

en

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

ce qui, pour les grandes chaînes de caractères, est nettement plus efficace. Pour autant que je sache, cela ne se produit pas lorsque vous utilisez la méthode concat.

Toutefois, la méthode concat est plus efficace pour concaténer 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 de concat pour le confirmer.

Donc, si vous êtes très soucieux de l'efficacité, vous devriez utiliser la méthode concat pour concaténer des chaînes de caractères éventuellement vides, et utiliser + dans les autres cas. Cependant, la différence de performance devrait être négligeable et vous ne devriez probablement jamais vous soucier de cela.

50voto

sundae1888 Points 788

J'ai effectué un test similaire à celui de @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() également. Chaque test a été effectué 10 fois, avec 100 000 répétitions pour chaque série. Voici les résultats :

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

Je n'ai pas encore décompilé la classe pour voir les internes ou l'exécuter avec le profileur, mais je soupçonne que a += b passe une grande partie de son temps à créer de nouveaux objets de type StringBuilder et ensuite les reconvertir en String .

23voto

Jason Cohen Points 36475

Tom a raison de décrire exactement ce que fait l'opérateur +. Il crée un fichier temporaire StringBuilder ajoute les parties, et termine par toString() .

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

@ marcio : Vous avez créé un micro-benchmark Avec les JVM modernes, ce n'est pas une façon valable de profiler du code.

La raison pour laquelle l'optimisation à l'exécution est importante est que beaucoup de ces différences dans le code -- y compris la création d'objets -- sont complètement différentes une fois que HotSpot est lancé. La seule façon d'en être sûr est de profiler votre code. in situ .

Enfin, toutes ces méthodes sont en fait incroyablement rapides. Il s'agit peut-être d'un cas d'optimisation prématurée. Si vous avez un code qui concatène beaucoup de chaînes de caractères, la façon d'obtenir une vitesse maximale n'a probablement rien à voir avec les opérateurs que vous choisissez et plutôt avec l'algorithme que vous utilisez !

21voto

Marcio Aguiar Points 6715

Que diriez-vous d'un simple test ? J'ai 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);
  • El "a + b" version exécutée en 2500ms .
  • El a.concat(b) exécuté en 1200ms .

Testé plusieurs fois. Le site concat() l'exécution de la version a pris la moitié du temps en moyenne.

Ce résultat m'a surpris car le concat() crée toujours une nouvelle chaîne de caractères (elle renvoie un " new String(result) ". C'est bien connu :

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

Pourquoi le compilateur n'était-il pas capable d'optimiser la création de chaînes de caractères dans le code "a + b", sachant qu'il aboutissait toujours à la même chaîne ? Il pourrait éviter la création d'une nouvelle chaîne. Si vous ne croyez pas à l'affirmation ci-dessus, testez par vous-même.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X