Pourquoi utiliser SsortingngBuffer dans Java au lieu de l’opérateur de concaténation de chaîne

Quelqu’un m’a dit qu’il était plus efficace d’utiliser SsortingngBuffer pour concaténer des chaînes en Java que d’utiliser l’opérateur + pour Ssortingng s. Qu’est-ce qui se passe sous le capot lorsque vous faites cela? Que fait SsortingngBuffer différemment?

Il est préférable d’utiliser SsortingngBuilder (c’est une version non synchronisée; quand construisez-vous des chaînes en parallèle?), Dans presque tous les cas, mais voici ce qui se passe:

Lorsque vous utilisez + avec deux chaînes, il comstack du code comme ceci:

 Ssortingng third = first + second; 

À quelque chose comme ça:

 SsortingngBuilder builder = new SsortingngBuilder( first ); builder.append( second ); third = builder.toSsortingng(); 

Par conséquent, pour de petits exemples, cela ne fait généralement pas de différence. Mais lorsque vous construisez une chaîne complexe, vous avez souvent beaucoup plus à gérer que cela; Par exemple, vous utilisez peut-être plusieurs instructions supplémentaires ou une boucle comme celle-ci:

 for( Ssortingng str : ssortingngs ) { out += str; } 

Dans ce cas, une nouvelle instance de SsortingngBuilder et une nouvelle Ssortingng (la nouvelle valeur de outSsortingng s sont immuables) sont requirejses dans chaque itération. C’est très inutile. Remplacer ceci par un seul SsortingngBuilder signifie que vous pouvez simplement produire une seule Ssortingng et ne pas remplir le tas avec Ssortingng qui ne vous intéresse pas.

Pour des concaténations simples comme:

 Ssortingng s = "a" + "b" + "c"; 

Il est plutôt inutile d’utiliser SsortingngBuffer – comme jodonnell l’a souligné, il sera intelligemment traduit en:

 Ssortingng s = new SsortingngBuffer().append("a").append("b").append("c").toSsortingng(); 

MAIS il est très difficile de concaténer des chaînes dans une boucle, comme:

 Ssortingng s = ""; for (int i = 0; i < 10; i++) { s = s + Integer.toString(i); } 

L'utilisation de ssortingng dans cette boucle génèrera 10 objects de chaîne intermédiaires en mémoire: "0", "01", "012", etc. En écrivant le même avec SsortingngBuffer vous suffit de mettre à jour un tampon interne de SsortingngBuffer et vous ne créez pas ces objects de chaîne intermédiaires dont vous n'avez pas besoin:

 SsortingngBuffer sb = new SsortingngBuffer(); for (int i = 0; i < 10; i++) { sb.append(i); } 

En fait, dans l'exemple ci-dessus, vous devriez utiliser SsortingngBuilder (introduit dans Java 1.5) au lieu de SsortingngBuffer - SsortingngBuffer est un peu plus lourd car toutes ses méthodes sont synchronisées.

L’un ne devrait pas être plus rapide que l’autre. Cela n’était pas vrai avant Java 1.4.2, car lors de la concaténation de plus de deux chaînes à l’aide de l’opérateur “+”, des objects Ssortingng intermédiaires seraient créés pendant le processus de création de la chaîne finale.

Cependant, comme le stipule JavaDoc pour SsortingngBuffer , au moins depuis Java 1.4.2, en utilisant l’opérateur “+”, la compilation consiste à créer un SsortingngBuffer et à y append() les nombreuses chaînes. Donc, pas de différence, apparemment.

Cependant, soyez prudent lorsque vous ajoutez une chaîne à une autre dans une boucle! Par exemple:

 Ssortingng mySsortingng = ""; for (Ssortingng s : listOfSsortingngs) { // Be careful! You're creating one intermediate Ssortingng object // for every iteration on the list (this is costly!) mySsortingng += s; } 

Gardez à l’esprit, cependant, que la concaténation de quelques chaînes avec “+” est plus propre que l’ append() toutes.

Sous le capot, il crée et ajoute un SsortingngBuffer, appelant toSsortingng () sur le résultat. Donc, peu importe ce que vous utilisez maintenant.

Alors

 Ssortingng s = "a" + "b" + "c"; 

devient

 Ssortingng s = new SsortingngBuffer().append("a").append("b").append("c").toSsortingng(); 

C’est vrai pour un tas d’ajouts en ligne dans une seule déclaration. Si vous construisez votre chaîne au cours de plusieurs instructions, vous perdez de la mémoire et SsortingngBuffer ou SsortingngBuilder est votre meilleur choix.

Je pense que si jdk1.5 (ou supérieur) et votre concaténation sont compatibles avec les threads, vous devriez utiliser SsortingngBuilder au lieu de SsortingngBuffer http://java4ever.blogspot.com/2007/03/ssortingng-vs-ssortingngbuffer-vs-ssortingngbuilder.html Quant aux gains en rapidité: http://www.about280.com/ssortingngtest.html

Personnellement, je code pour la lisibilité, donc à moins que vous trouviez que la concaténation de chaînes rend votre code considérablement plus lent, restz avec la méthode qui rend votre code plus lisible.

Dans certains cas, ceci est obsolète en raison des optimisations effectuées par le compilateur, mais le problème général est que ce code ressemble à:

 ssortingng mySsortingng=""; for(int i=0;i 

agira comme ci-dessous (chaque étape étant la prochaine itération de la boucle):

  1. construire un object chaîne de longueur 1 et la valeur "x"
  2. Créez un nouvel object chaîne de taille 2, copiez-y l'ancienne chaîne "x", ajoutez "x" en position 2.
  3. Créez un nouvel object chaîne de taille 3, copiez-y l'ancienne chaîne "xx", ajoutez "x" en position 3.
  4. ... etc

Comme vous pouvez le voir, chaque itération doit copier un caractère de plus, ce qui nous permet d'exécuter 1 + 2 + 3 + 4 + 5 + ... + N opérations par boucle. Ceci est une opération O (n ^ 2). Si toutefois nous savions à l'avance que nous n'avions besoin que de N caractères, nous pourrions le faire en une seule allocation, avec une copie de seulement N caractères des chaînes que nous utilisions - une simple opération O (n).

SsortingngBuffer / SsortingngBuilder évite cela car ils sont modifiables et n'ont donc pas besoin de continuer à copier les mêmes données encore et encore (tant qu'il y a de l'espace à copier dans leur tampon interne). Ils évitent de procéder à une allocation et à une copie proportionnelles au nombre d’ajouts effectués en sur-affectant leur mémoire tampon d’une proportion de leur taille actuelle, ce qui donne des O (1) amortis supplémentaires.

Cependant, il convient de noter que, souvent, le compilateur peut optimiser le code dans le style SsortingngBuilder (ou mieux, car il peut effectuer un pliage constant, etc.) automatiquement.

Java transforme ssortingng1 + ssortingng2 en une construction SsortingngBuffer, append () et toSsortingng (). C’est logique.

Cependant, dans Java 1.4 et les versions antérieures, cela se ferait séparément pour chaque opérateur dans l’instruction. Cela signifiait que faire un + b + c entraînerait deux constructions SsortingngBuffer avec deux appels toSsortingng (). Si vous aviez une longue série de concats, cela deviendrait un vrai désordre. Le faire vous-même signifiait que vous pouviez contrôler cela et le faire correctement.

Java 5.0 et les versions ultérieures semblent le faire plus judicieusement, ce qui est moins problématique et certainement moins verbeux.

AFAIK cela dépend de la version de JVM, dans les versions antérieures à 1.5 en utilisant “+” ou “+ =” réellement copié la chaîne entière à chaque fois.

Attention, l’utilisation de + = alloue réellement la nouvelle copie de chaîne.

Comme cela a été souligné, l’utilisation de boucles + in implique la copie.

Lorsque les chaînes qui sont mises en correspondance sont des constantes de temps de compilation, elles sont concaténées au moment de la compilation.

 Ssortingng foo = "a" + "b" + "c"; 

A est compilé pour:

 Ssortingng foo = "abc"; 

SsortingngBuffer est mutable. Il ajoute la valeur de la chaîne au même object sans instancier un autre object. Faire quelque chose comme:

 mySsortingng = mySsortingng + "XYZ" 

va créer un nouvel object Ssortingng.

Pour concaténer deux chaînes à l’aide de «+», une nouvelle chaîne doit être allouée avec un espace pour les deux chaînes, puis les données sont copiées à partir des deux chaînes. Un SsortingngBuffer est optimisé pour la concaténation et alloue plus d’espace que nécessaire. Lorsque vous concaténez une nouvelle chaîne, dans la plupart des cas, les caractères peuvent simplement être copiés à la fin du tampon de chaîne existant.
Pour concaténer deux chaînes, l’opérateur ‘+’ aura probablement moins de charge, mais à mesure que vous concaténerez plusieurs chaînes, SsortingngBuffer deviendra plus performant, utilisant moins d’allocations de mémoire et moins de copies de données.

La classe SsortingngBuffer maintient un tableau de caractères pour contenir le contenu des chaînes que vous concaténez, tandis que la méthode + crée une nouvelle chaîne à chaque fois qu’elle est appelée et ajoute les deux parameters (param1 + param2).

Le SsortingngBuffer est plus rapide car il peut être capable d’utiliser son tableau déjà existant pour concaténer / stocker toutes les chaînes. 2. même s’ils ne rentrent pas dans le tableau, il est plus rapide d’allouer un tableau de sauvegarde plus grand, puis de générer de nouveaux objects Ssortingng pour chaque évocation.

Les chaînes étant immuables, chaque appel à l’opérateur + crée un nouvel object Ssortingng et copie les données Ssortingng sur la nouvelle chaîne. Étant donné que la copie d’une chaîne prend du temps linéaire dans la longueur de la chaîne, une séquence de N appels à l’opérateur + entraîne un temps d’exécution de O (N 2 ) (quadratique).

Inversement, comme SsortingngBuffer est mutable, il n’est pas nécessaire de copier la chaîne à chaque fois que vous effectuez un Append (), donc une séquence de N appels Append () prend le temps O (N) (linéaire). Cela ne fait qu’une différence significative dans l’exécution si vous ajoutez un grand nombre de chaînes.

Comme dit précédemment, l’object Ssortingng est immuable, ce qui signifie qu’une fois créé (voir ci-dessous), il ne peut pas être modifié.

Ssortingng x = new Ssortingng (“quelque chose”); // ou

Ssortingng x = “quelque chose”;

Ainsi, lorsque vous tentez de concentrer des objects Ssortingng, la valeur de ces objects est prise et placée dans un nouvel object Ssortingng.

Si vous utilisez plutôt le SsortingngBuffer, qui est modifiable par IS, vous ajoutez continuellement les valeurs à une liste interne de char (primitives), qui peut être étendue ou tronquée pour correspondre à la valeur requirejse. Aucun nouvel object n’est créé, seuls les nouveaux caractères sont créés / supprimés pour contenir les valeurs.

Lorsque vous concaténez deux chaînes, vous créez en réalité un troisième object Ssortingng en Java. Utiliser SsortingngBuffer (ou SsortingngBuilder en Java 5/6), est plus rapide car il utilise un tableau interne de caractères pour stocker la chaîne, et lorsque vous utilisez l’une de ses méthodes add (…), il ne crée pas de nouvelle chaîne. object. Au lieu de cela, SsortingngBuffer / Buider ajoute le tableau interne.

Dans les concaténations simples, il n’est pas vraiment difficile de concaténer des chaînes à l’aide de SsortingngBuffer / Builder ou de l’opérateur ‘+’, mais lorsque vous faites beaucoup de concaténations de chaînes, vous verrez que l’utilisation de SsortingngBuffer / Builder est beaucoup plus rapide.

Informations complémentaires:

SsortingngBuffer est une classe thread-safe

 public final class SsortingngBuffer extends AbstractSsortingngBuilder implements Serializable, CharSequence { // .. skip .. public synchronized SsortingngBuffer append(SsortingngBuffer ssortingngbuffer) { super.append(ssortingngbuffer); return this; } // .. skip .. } 

Mais SsortingngBuilder n’est pas compatible avec les threads, il est donc plus rapide d’utiliser SsortingngBuilder si possible

 public final class SsortingngBuilder extends AbstractSsortingngBuilder implements Serializable, CharSequence { // .. skip .. public SsortingngBuilder append(Ssortingng s) { super.append(s); return this; } // .. skip .. } 

Les chaînes étant imputables en Java, chaque fois que vous concannez une chaîne, un nouvel object est créé en mémoire. SpringBuffer utilise le même object en mémoire.

Je pense que la réponse la plus simple est la suivante: c’est plus rapide.

Si vous voulez vraiment connaître toutes les choses sous le capot, vous pouvez toujours regarder la source vous-même:

http://www.sun.com/software/opensource/java/getinvolved.jsp

http://download.java.net/jdk6/latest/archive/

La section Ssortingng Concatenation Operator + de la spécification de langage Java vous donne plus d’informations sur les raisons pour lesquelles l’opérateur + peut être si lent.