222 votes

String, StringBuffer, et StringBuilder

Veuillez me donner une situation réelle pour comparer String , StringBuffer y StringBuilder ?

393voto

bakkal Points 13449

Différence de mutabilité :

String es immuable si vous essayez de modifier leurs valeurs, un autre objet est créé, alors que StringBuffer y StringBuilder sont mutable pour qu'ils puissent changer leurs valeurs.

La différence entre le fil et la sécurité :

La différence entre StringBuffer y StringBuilder c'est que StringBuffer est à l'abri des fils. Ainsi, lorsque l'application ne doit être exécutée que dans un seul thread, il est préférable d'utiliser la fonction StringBuilder . StringBuilder est plus efficace que StringBuffer .

Situations :

  • Si votre chaîne de caractères ne doit pas changer, utilisez une classe String car une classe String est immuable.
  • Si votre chaîne de caractères peut changer (exemple : beaucoup de logique et d'opérations dans la construction de la chaîne de caractères) et que vous n'y accéderez qu'à partir d'un seul thread, l'utilisation d'un fichier de type StringBuilder est suffisant.
  • Si votre chaîne de caractères peut changer et qu'elle est accessible à partir de plusieurs threads, utilisez une balise StringBuffer parce que StringBuffer est synchrone, ce qui permet d'assurer la sécurité des fils.

18 votes

De plus, l'utilisation de String pour des opérations logiques est plutôt lente, et n'est pas du tout conseillée, puisque la JVM convertit le String en StringBuffer dans le bytecode. Une grande partie de la charge de travail est perdue lors de la conversion de String en StringBuffer, puis à nouveau en String.

2 votes

Ainsi, en Strings lorsque nous modifions la valeur, un autre objet est créé. Est-ce que la référence de l'ancien objet est annulée afin qu'elle puisse être ramassée par le système de gestion des déchets ? GC ou est-ce qu'il y a même un ramassage des déchets ?

0 votes

@PietervanNiekerk Qu'entendez-vous par opérations logiques ?

48voto

Artefacto Points 50896
  • Vous utilisez String lorsqu'une structure immuable est appropriée ; l'obtention d'une nouvelle séquence de caractères à partir d'une String peut entraîner une pénalité de performance inacceptable, que ce soit en temps CPU ou en mémoire (l'obtention de sous-chaînes est efficace pour le CPU car les données ne sont pas copiées, mais cela signifie qu'une quantité potentiellement beaucoup plus importante de données peut rester allouée).
  • Vous utilisez StringBuilder lorsque vous devez créer une séquence de caractères mutable, généralement pour concaténer plusieurs séquences de caractères ensemble.
  • Vous utilisez StringBuffer dans les mêmes circonstances que vous utiliseriez StringBuilder mais lorsque les modifications apportées à la chaîne sous-jacente doivent être synchronisées (parce que plusieurs threads lisent/modifient le tampon de la chaîne).

Voir un exemple aquí .

2 votes

Concis, mais incomplet, il manque la raison fondamentale d'utiliser StringBuilder/Buffer et c'est pour réduire ou éliminer la réallocation et la copie de tableau du comportement régulier de concaténation de String.

1 votes

"Vous utilisez String quand vous avez affaire à des chaînes immuables" - cela n'a pas de sens. Les instances de String SONT immuables, donc peut-être que le commentaire devrait être "Utilisez String quand l'utilisation de la mémoire due à l'immuabilité n'a pas d'importance". La réponse acceptée couvre assez bien les bases.

28voto

Jarrod Roberson Points 32263

L'essentiel :

String est une classe immuable, elle ne peut pas être modifiée. StringBuilder est une classe mutable qui peut être ajoutée, remplacée ou supprimée par des caractères, et finalement convertie en une classe String StringBuffer est la version originale synchronisée de StringBuilder

Vous devriez préférer StringBuilder dans tous les cas où un seul thread accède à votre objet.

Les détails :

Notez également que StringBuilder/Buffers ne sont pas magiques, elles utilisent simplement un tableau comme objet de sauvegarde et ce tableau doit être réaffecté chaque fois qu'il est plein. Assurez-vous de créer votre StringBuilder/Buffer des objets suffisamment grands à l'origine pour qu'ils ne doivent pas être constamment redimensionnés à chaque fois. .append() est appelé.

Le redimensionnement peut devenir très dégénéré. En fait, il redimensionne le tableau de sauvegarde à deux fois sa taille actuelle chaque fois qu'il doit être étendu. Cela peut conduire à l'allocation de grandes quantités de RAM qui ne sont pas utilisées lorsque StringBuilder/Buffer les classes commencent à se développer.

En Java String x = "A" + "B"; utilise un StringBuilder dans les coulisses. Ainsi, pour les cas simples, il n'y a aucun avantage à déclarer les vôtres. Mais si vous construisez String qui sont grands, disons moins de 4k, alors déclarer StringBuilder sb = StringBuilder(4096); est beaucoup plus efficace que la concaténation ou l'utilisation de la fonction Constructeur par défaut qui ne comporte que 16 caractères. Si votre String va être inférieure à 10k, alors initialisez-la avec le constructeur à 10k pour être sûr. Mais s'il est initialisé à 10k et que vous écrivez un caractère de plus que 10k, il sera réaffecté et copié dans un tableau de 20k. Il est donc préférable de l'initialiser à une valeur élevée plutôt qu'à une valeur faible.

Dans le cas de la redimensionnement automatique, au 17e caractère, le tableau de sauvegarde est réaffecté et copié sur 32 caractères, au 33e caractère, cela se produit à nouveau et vous devez réaffecter et copier le tableau sur 64 caractères. Vous pouvez voir comment cela dégénère en lots de réallocations et de copies, ce qui est ce que vous essayez vraiment d'éviter en utilisant StringBuilder/Buffer en premier lieu.

Ceci est extrait du code source du JDK 6 pour AbstractStringBuilder.

   void expandCapacity(int minimumCapacity) {
    int newCapacity = (value.length + 1) * 2;
        if (newCapacity < 0) {
            newCapacity = Integer.MAX_VALUE;
        } else if (minimumCapacity > newCapacity) {
        newCapacity = minimumCapacity;
    }
        value = Arrays.copyOf(value, newCapacity);
    }

Une bonne pratique consiste à initialiser le StringBuilder/Buffer un peu plus grand que ce dont vous pensez avoir besoin si vous ne connaissez pas d'emblée la taille de l'appareil. String sera mais vous pouvez le deviner. Une allocation d'un peu plus de mémoire que ce dont vous avez besoin est préférable à de nombreuses réallocations et copies.

Attention également à l'initialisation d'un StringBuilder/Buffer avec un String car cela n'allouera que la taille de la chaîne + 16 caractères, ce qui, dans la plupart des cas, ne fera que lancer le cycle dégénéré de réallocation et de copie que vous essayez d'éviter. Ce qui suit est tiré directement du code source de Java 6.

public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
    }

Si par hasard vous vous retrouvez avec une instance de StringBuilder/Buffer que vous n'avez pas créé et que vous ne pouvez pas contrôler le constructeur qui est appelé, il existe un moyen d'éviter le comportement dégénéré de réaffectation et de copie. Appelez .ensureCapacity() avec la taille que vous voulez pour assurer que votre résultat String s'adaptera.

Les alternatives :

Juste comme une note, si vous faites vraiment lourd String et la manipulation, il existe une alternative beaucoup plus orientée vers les performances appelée Cordes .

Une autre solution consiste à créer un StringList mise en œuvre par sous-classement ArrayList<String> et l'ajout de compteurs pour suivre le nombre de caractères sur chaque page. .append() et d'autres opérations de mutation de la liste, alors remplacez .toString() pour créer un StringBuilder de la taille exacte dont vous avez besoin, puis parcourir la liste en boucle et construire la sortie. StringBuilder une variable d'instance et mettre en cache les résultats de l'opération. .toString() et n'avoir à le générer à nouveau que lorsque quelque chose change.

N'oubliez pas non plus String.format() lors de la construction d'une sortie formatée fixe, qui peut être optimisée par le compilateur au fur et à mesure qu'il l'améliore.

1 votes

Fait String x = "A" + "B"; compile réellement pour être un StringBuilder ? Pourquoi ne compilerait-il pas simplement en String x = "AB"; il ne devrait utiliser un StringBuilder que si les composants ne sont pas connus au moment de la compilation.

0 votes

Il se peut qu'il optimise les constantes String, je ne me souviens pas de la dernière fois où j'ai décompilé le code d'octet, mais je sais que s'il y a des variables, il utilisera une implémentation StringBuilder à coup sûr. Vous pouvez télécharger le code source du JDK et le découvrir par vous-même. "A" + "B" est un exemple artificiel, c'est certain.

0 votes

Je m'interrogeais sur String.format(). Je ne l'ai jamais vraiment vu être utilisé dans les projets. D'habitude c'est le StringBuilder. Enfin, en général c'est plutôt "A" + "B" + "C" parce que les gens sont paresseux ;) J'avais tendance à toujours utiliser un StringBuilder, même s'il ne s'agissait que de deux chaînes concaténées, parce qu'à l'avenir, d'autres chaînes seraient peut-être ajoutées. Je n'ai jamais utilisé String.format() principalement parce que je ne me suis jamais souvenu de quel JDK il a été introduit - je vois que c'est JDK1.5, je l'utiliserais en faveur des autres options.

9voto

OscarRyz Points 82553

Vous voulez dire, pour la concaténation ?

Exemple concret : Vous voulez créer une nouvelle chaîne de caractères à partir de plusieurs autres. .

Par exemple, pour envoyer un message :

Chaîne de caractères

String s = "Dear " + user.name + "<br>" + 
" I saw your profile and got interested in you.<br>" +
" I'm  " + user.age + "yrs. old too"

StringBuilder

String s = new StringBuilder().append.("Dear ").append( user.name ).append( "<br>" ) 
          .append(" I saw your profile and got interested in you.<br>") 
          .append(" I'm  " ).append( user.age ).append( "yrs. old too")
          .toString()

Ou

String s = new StringBuilder(100).appe..... etc. ...
// The difference is a size of 100 will be allocated upfront as  fuzzy lollipop points out.

StringBuffer ( la syntaxe est exactement la même qu'avec StringBuilder, les effets diffèrent )

À propos de

StringBuffer vs. StringBuilder

Le premier est synchronisé et le second ne l'est pas.

Ainsi, si vous l'invoquez plusieurs fois dans un même thread (ce qui est le cas dans 90% des cas), StringBuilder fonctionnera beaucoup plus vite parce qu'il ne s'arrêtera pas pour voir si le frein à filet lui appartient.

Il est donc recommandé d'utiliser StringBuilder (à moins, bien sûr, que plusieurs fils n'y accèdent en même temps, ce qui est rare).

String concaténation ( en utilisant l'opérateur + ) peut être optimisé par le compilateur afin d'utiliser la fonction StringBuilder Dans les premiers jours de Java, c'était quelque chose que tout le monde disait qu'il fallait éviter à tout prix, car chaque concaténation créait un nouvel objet String. Les compilateurs modernes ne font plus cela, mais c'est quand même une bonne pratique d'utiliser StringBuilder à la place, au cas où vous utiliseriez un "vieux" compilateur.

modifier

Pour ceux qui sont curieux, voici ce que fait le compilateur pour cette classe :

class StringConcatenation {
    int x;
    String literal = "Value is" + x;
    String builder = new StringBuilder().append("Value is").append(x).toString();
}

javap -c StringConcatenation

Compiled from "StringConcatenation.java"
class StringConcatenation extends java.lang.Object{
int x;

java.lang.String literal;

java.lang.String builder;

StringConcatenation();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   new #2; //class java/lang/StringBuilder
   8:   dup
   9:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   12:  ldc #4; //String Value is
   14:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   17:  aload_0
   18:  getfield    #6; //Field x:I
   21:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   24:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   27:  putfield    #9; //Field literal:Ljava/lang/String;
   30:  aload_0
   31:  new #2; //class java/lang/StringBuilder
   34:  dup
   35:  invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   38:  ldc #4; //String Value is
   40:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   43:  aload_0
   44:  getfield    #6; //Field x:I
   47:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   50:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   53:  putfield    #10; //Field builder:Ljava/lang/String;
   56:  return

}

Les lignes numérotées de 5 à 27 sont pour la chaîne nommée "littérale".

Les lignes numérotées 31-53 sont pour le String nommé "builder".

Il n'y a pas de différence, exactement la même même est exécuté pour les deux chaînes de caractères.

2 votes

C'est un très mauvais exemple. Initialiser le StringBuilder avec " Dear " signifie que le premier .append() provoquera une réallocation et une copie. Cela annule complètement l'efficacité que vous essayez de gagner par rapport à une concaténation "normale". Un meilleur exemple serait de le créer avec une taille initiale qui contiendrait le contenu entier de la chaîne finale.

1 votes

En règle générale, il n'est PAS bon d'utiliser un fichier de type StringBuilder pour faire la concaténation des chaînes sur le côté droit de l'affectation. Toute bonne implémentation utilisera un StringBuilder dans les coulisses comme vous le dites. De plus, votre exemple de "a" + "b" serait compilé en un seul littéral "ab" mais si vous avez utilisé StringBuilder cela entraînerait deux appels inutiles à append() .

0 votes

@Mark je ne voulais pas utiliser "a"+"b" mais pour dire ce qui était un Concaténation de chaînes de caractères si je le change pour être explicite. Ce que vous ne dites pas, c'est pourquoi ce n'est pas une bonne pratique de le faire. C'est exactement ce que fait un compilateur (moderne). @fuzzy, je suis d'accord, surtout quand on sait quelle sera la taille de la chaîne finale (approximativement).

3voto

Hitesh Garg Points 14

La différence entre String et les deux autres classes est que String est immuable et les deux autres classes sont mutables.

Mais pourquoi avons-nous deux classes pour le même objectif ?

La raison en est que StringBuffer est un fil sûr et StringBuilder ne l'est pas. StringBuilder est une nouvelle classe sur StringBuffer Api et il a été introduit dans JDK5 et est toujours recommandé si vous travaillez dans un environnement à un seul fil, car il est beaucoup plus facile à utiliser. Faster

Pour plus de détails, vous pouvez lire http://www.codingeek.com/java/stringbuilder-and-stringbuffer-a-way-to-create-mutable-strings-in-java/

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