String, StringBuffer et StringBuilder

213

S'il vous plaît me dire une situation en temps réel pour comparer String, StringBufferet StringBuilder?

JavaUser
la source

Réponses:

378

Différence de mutabilité:

Stringest immuable , si vous essayez de modifier leurs valeurs, un autre objet est créé, tandis que StringBufferet StringBuildersont mutables afin qu'elles puissent changer leurs valeurs.

Différence de sécurité de fil:

La différence entre StringBufferet StringBuilderest que StringBufferc'est thread-safe. Ainsi, lorsque l'application doit être exécutée uniquement dans un seul thread, il est préférable de l'utiliser StringBuilder. StringBuilderest plus efficace que StringBuffer.

Situations:

  • Si votre chaîne ne change pas, utilisez une classe String car un Stringobjet est immuable.
  • Si votre chaîne peut changer (exemple: beaucoup de logique et d'opérations dans la construction de la chaîne) et ne sera accessible qu'à partir d'un seul thread, utiliser un StringBuilderest suffisant.
  • Si votre chaîne peut changer et sera accessible à partir de plusieurs threads, utilisez un StringBuffercar StringBufferest synchrone afin que vous ayez la sécurité des threads.
Bakkal
la source
16
De plus, l'utilisation de String pour les opérations logiques est plutôt lente et déconseillée du tout, car la JVM convertit la String en StringBuffer dans le bytecode. Beaucoup de frais généraux sont gaspillés lors de la conversion de String en StringBuffer, puis de nouveau en String.
Pieter van Niekerk
2
Ainsi, Stringslorsque nous modifions la valeur, un autre objet est créé. L'ancienne référence d'objet est-elle annulée afin qu'elle puisse être récupérée par le GCou est-elle même récupérée?
Harsh Vardhan
@PietervanNiekerk Que voulez-vous dire par opérations logiques?
emlai
Les opérations logiques que je veux dire sont des chaînes de base, maintenant une chose que je voudrais demander, comme indiqué par @Peter devrions-nous commencer à utiliser StringBuffer à la place sur String dans tous les cas ou il y a des cas spécifiques?
JavaDragon
@bakkal Puis-je utiliser toutes les méthodes de Stringavec StringBuilder?
roottraveller
47
  • Vous utilisez Stringlorsqu'une structure immuable est appropriée; l'obtention d'une nouvelle séquence de caractères à partir d'un Stringpeut 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 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 StringBuilderlorsque vous devez créer une séquence de caractères modifiable, généralement pour concaténer plusieurs séquences de caractères ensemble.
  • Vous utilisez StringBufferdans les mêmes circonstances que vous utiliseriez StringBuilder, mais lorsque les modifications apportées à la chaîne sous-jacente doivent être synchronisées (car plusieurs threads lisent / modifient le tampon de chaîne).

Voir un exemple ici .

Artefacto
la source
2
concis, mais incomplet, il manque la raison fondamentale d'utiliser StringBuilder / Buffer et qui est de réduire ou d'éliminer la réallocation et la copie de tableau du comportement de concaténation String standard.
1
"Vous utilisez String lorsque vous traitez avec des chaînes immuables" - n'a pas de sens. Les instances de String SONT immuables, donc le commentaire devrait peut-être lire "Utiliser String lorsque l'utilisation de la mémoire en raison de l'immuabilité n'a pas d'importance". La réponse acceptée couvre assez bien ses bases.
fooMonster
28

Les bases:

Stringest une classe immuable, elle ne peut pas être modifiée. StringBuilderest une classe modifiable qui peut être ajoutée, les caractères remplacés ou supprimés et finalement convertis en String StringBufferest la version synchronisée d'origine deStringBuilder

Vous devriez préférer StringBuilderdans tous les cas où vous n'avez qu'un seul thread accéder à votre objet.

Les détails:

Notez également que ce StringBuilder/Buffersn'est pas magique, ils utilisent simplement un tableau comme objet de support et que le tableau doit être réalloué chaque fois qu'il est plein. Soyez sûr et créez vos StringBuilder/Bufferobjets assez grands à l'origine où ils n'ont pas besoin d'être constamment redimensionnés à chaque .append()appel.

Le redimensionnement peut devenir très dégénéré. Il redimensionne fondamentalement le tableau de sauvegarde à 2 fois sa taille actuelle chaque fois qu'il doit être étendu. Cela peut entraîner l'allocation de grandes quantités de RAM et leur non-utilisation lorsque les StringBuilder/Bufferclasses commencent à grossir.

En Java String x = "A" + "B";utilise un StringBuilderdans les coulisses. Donc, pour les cas simples, il n'y a aucun avantage à déclarer le vôtre. Mais si vous construisez des Stringobjets qui sont grands, disons moins de 4k, alors déclarer StringBuilder sb = StringBuilder(4096);est beaucoup plus efficace que la concaténation ou utiliser le constructeur par défaut qui n'est que de 16 caractères. Si votre Stringva être inférieur à 10k, initialisez-le avec le constructeur à 10k pour être sûr. Mais s'il est initialisé à 10k alors vous écrivez 1 caractère de plus que 10k, il sera réalloué et copié dans un tableau de 20k. Il est donc préférable d'initialiser haut que bas.

Dans le cas du redimensionnement automatique, au 17e caractère, le tableau de sauvegarde est réalloué et copié sur 32 caractères, au 33e caractère, cela se produit à nouveau et vous pouvez réallouer et copier le tableau en 64 caractères. Vous pouvez voir comment cela dégénère en beaucoup de réallocations et de copies, ce que vous essayez vraiment d'éviter d'utiliser StringBuilder/Bufferen premier lieu.

Ceci provient du code source 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 meilleure pratique consiste à initialiser StringBuilder/Bufferun peu plus grand que vous pensez que vous aurez besoin si vous ne savez pas tout de suite quelle Stringsera la taille mais vous pouvez le deviner. Une allocation de mémoire légèrement supérieure à celle dont vous avez besoin sera meilleure que de nombreuses réallocations et copies.

Méfiez-vous également d'initialiser un StringBuilder/Bufferavec un Stringcar cela n'allouera que la taille de la chaîne + 16 caractères, ce qui dans la plupart des cas ne fera que démarrer le cycle de réallocation et de copie dégénéré que vous essayez d'éviter. Ce qui suit est directement issu du code source Java 6.

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

Si, par hasard, vous vous retrouvez avec une instance StringBuilder/Bufferque vous n'avez pas créée et que vous ne pouvez pas contrôler le constructeur appelé, il existe un moyen d'éviter le comportement dégénéré de réallocation et de copie. Appelez .ensureCapacity()avec la taille que vous souhaitez pour vous assurer que votre résultat Strings'intégrera.

Les alternatives:

Tout comme une note, si vous faites de la String construction et de la manipulation très lourdes , il existe une alternative beaucoup plus axée sur les performances appelée Cordes .

Une autre alternative consiste à créer une StringListimplémentation en sous-classant ArrayList<String>et en ajoutant des compteurs pour suivre le nombre de caractères sur chaque .append()opération de mutation de la liste, puis remplacer .toString()pour créer une StringBuildertaille exacte dont vous avez besoin et parcourir la liste et construire la sortie, vous pouvez même en faire StringBuilderune variable d'instance et «mettre en cache» les résultats .toString()et ne devez la régénérer que lorsque quelque chose change.

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

utilisateur177800
la source
1
Compile-t-il String x = "A" + "B";vraiment pour être un StringBuilder? Pourquoi ne compilerait-il pas simplement String x = "AB";, il ne devrait utiliser un StringBuilder que si les composants ne sont pas connus au moment de la compilation.
Matt Greer
cela pourrait optimiser les constantes String, je ne me souviens pas de la dernière fois que le code d'octet a été décompilé, 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 JDK et découvrir par vous-même. "A" + "B" est un exemple artificiel à coup sûr.
Je me posais des questions sur String.format (). Je ne l'ai jamais vraiment vu utilisé dans des projets. Il s'agit généralement du StringBuilder. Eh bien, en général, c'est en fait "A" + "B" + "C" parce que les gens sont paresseux;) J'ai eu tendance à toujours utiliser un StringBuilder, même si ce n'était que deux chaînes concaténées, car à l'avenir, peut-être plus de chaînes seraient 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.
jamiebarrow
9

Voulez-vous dire, pour la concaténation?

Exemple réel: vous souhaitez créer une nouvelle chaîne à partir de nombreuses autres .

Par exemple pour envoyer un message:

Chaîne

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 comme avec StringBuilder, les effets diffèrent)

À propos

StringBuffer contre. StringBuilder

Le premier est synchronisé et plus tard ne l'est pas.

Donc, si vous l'invoquez plusieurs fois dans un seul thread (ce qui représente 90% des cas), StringBuilderil s'exécutera beaucoup plus rapidement car il ne s'arrêtera pas pour voir s'il possède le verrou de thread.

Donc, il est recommandé de l'utiliser StringBuilder(à moins bien sûr que vous ayez plus d'un thread y accédant en même temps, ce qui est rare)

Stringla concaténation (en utilisant l'opérateur + ) peut être optimisée par le compilateur pour l'utiliser en StringBuilderdessous, donc, ce n'est plus quelque chose à craindre, dans les jours les plus anciens de Java, c'était quelque chose que tout le monde devrait éviter à tout prix, car chaque concaténation créé un nouvel objet String. Les compilateurs modernes ne le font plus, mais c'est toujours une bonne pratique à utiliser à la StringBuilderplace au cas où vous utilisez un "ancien" compilateur.

Éditer

Juste pour qui est curieux, voici ce que le compilateur fait 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 correspondent à la chaîne nommée "littéral"

Les lignes numérotées 31-53 correspondent à la chaîne nommée "builder"

Il n'y a aucune différence, exactement le même code est exécuté pour les deux chaînes.

OscarRyz
la source
2
c'est un très mauvais exemple. L'initialisation de StringBuilder avec "Dear" signifie que le premier .append () provoquera une réallocation et une copie. Nier complètement toute efficacité que vous essayez de gagner par rapport à la concaténation "normale". Un meilleur exemple serait de le créer avec une taille initiale qui contiendra tout le contenu de la chaîne finale.
1
Il n'est généralement pas recommandé d'utiliser une StringBuilderconcaténation de chaîne à faire 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 "a" + "b"serait compilé en un seul littéral, "ab"mais si vous l'utilisiez, StringBuilderil en résulterait deux appels inutiles à append().
Mark Peters
@Mark Je ne voulais pas utiliser "a"+"b"mais pour dire ce qu'était une concaténation de chaînes, je la 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). @ flou, je suis d'accord, surtout quand vous savez quelle serait la taille de la chaîne finale (aprox).
OscarRyz
1
Je ne pense pas que ce soit une pratique particulièrement mauvaise , mais je ne voudrais certainement pas qu'une recommandation fasse cela en général. C'est maladroit et difficile à lire, et trop verbeux. De plus, il encourage les erreurs comme la vôtre lorsque vous cassez deux littéraux qui pourraient autrement être compilés en un seul. Ce n'est que si le profilage m'a dit que cela faisait une différence que je le ferais à votre façon.
Mark Peters
@ Mark l'a compris. Je pensais plus dans le "gros morceau de code comme un modèle" et pas vraiment dans chaque littéral de chaîne régulière. Mais oui, je suis d'accord, car ils font la même chose de nos jours, cela n'a pas de sens (il y a 10 ans était une raison de rejeter un changement dans une révision de code) :)
OscarRyz
8
-------------------------------------------------- --------------------------------
                  String StringBuffer StringBuilder
-------------------------------------------------- --------------------------------                 
Zone de stockage | Tas de chaînes de pool de chaînes constantes
Modifiable | Non (immuable) Oui (mutable) Oui (mutable)
Thread Safe | Oui oui non
 Performance | Rapide Très lent Rapide
-------------------------------------------------- --------------------------------
Abhijit Maity
la source
Pourquoi les performances de String sont rapides et les performances de StringBuffer très lentes?
gaurav
1
@gaurav vous pouvez lire son code source, toutes les méthodes de StringBuffer sont synchronisedet c'est pourquoi .
Entendu
8

Famille de cordes

Chaîne

Le String classreprésente des chaînes de caractères. Tous les littéraux de chaîne du programme Java, tels que ceux qui "abc"sont implémentés en tant qu'instances de cette classe.

Les objets chaîne sont immuables une fois qu'ils sont créés, nous ne pouvons pas changer. (Les chaînes sont des constantes )

  • Si une chaîne est créée en utilisant constructeur ou méthode alors ces chaînes seront stockées dans Heap mémoire ainsi que SringConstantPool. Mais avant d'enregistrer dans le pool, il invoque une intern()méthode pour vérifier la disponibilité des objets avec le même contenu dans le pool en utilisant la méthode equals. Si la copie de chaîne est disponible dans le pool, renvoie la référence. Sinon, l'objet String est ajouté au pool et renvoie la référence.

    • Le langage Java fournit un support spécial pour l'opérateur de concaténation de chaînes ( +) et pour la conversion d'autres objets en chaînes. La concaténation de chaînes est implémentée via la classe StringBuilder (ou StringBuffer) et sa méthode append.

    String heapSCP = new String("Yash");
    heapSCP.concat(".");
    heapSCP = heapSCP + "M";
    heapSCP = heapSCP + 777;
    
    // For Example: String Source Code 
    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }
  • Les littéraux de chaîne sont stockés dans StringConstantPool.

    String onlyPool = "Yash";

StringBuilder et StringBuffer sont des séquences de caractères modifiables. Cela signifie que l'on peut changer la valeur de ces objets. StringBuffer a les mêmes méthodes que StringBuilder, mais chaque méthode de StringBuffer est synchronisée, elle est donc thread-safe.

  • Les données StringBuffer et StringBuilder ne peuvent être créées qu'en utilisant un nouvel opérateur. Ainsi, ils sont stockés dans la mémoire du tas.

  • Les instances de StringBuilder ne sont pas sûres pour une utilisation par plusieurs threads. Si une telle synchronisation est requise, il est recommandé d'utiliser StringBuffer.

    StringBuffer threadSafe = new StringBuffer("Yash");
    threadSafe.append(".M");
    threadSafe.toString();
    
    StringBuilder nonSync = new StringBuilder("Yash");
    nonSync.append(".M");
    nonSync.toString();
  • StringBuffer et StringBuilder ont des méthodes spéciales comme., replace(int start, int end, String str)Et reverse().

    REMARQUE : StringBuffer et SringBuilder sont mutables car ils fournissent l'implémentation de Appendable Interface.


Quand utiliser lequel?

  • Si vous n'allez pas changer la valeur à chaque fois, il vaut mieux utiliser String Class. Dans le cadre de Generics si vous souhaitez trier Comparable<T>ou comparer une valeur, optez pour String Class.

    //ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.Comparable
    Set<StringBuffer> set = new TreeSet<StringBuffer>();
    set.add( threadSafe );
    System.out.println("Set : "+ set);
  • Si vous allez modifier la valeur à chaque fois, optez pour StringBuilder qui est plus rapide que StringBuffer. Si plusieurs threads modifient la valeur, optez pour StringBuffer.

Yash
la source
4

En outre, StringBufferest thread-safe, ce qui StringBuildern'est pas le cas.

Ainsi, dans une situation en temps réel lorsque différents threads y accèdent, cela StringBuilderpourrait avoir un résultat non déterministe.

Lars Andren
la source
3

Notez que si vous utilisez Java 5 ou une version plus récente, vous devez utiliser à la StringBuilderplace de StringBuffer. Dans la documentation de l'API:

Avec la version 5 JDK, cette classe a été complétée par une classe équivalente conçue pour être utilisée par un seul fil, StringBuilder. La StringBuilderclasse doit généralement être utilisée de préférence à celle-ci, car elle prend en charge toutes les mêmes opérations mais elle est plus rapide, car elle n'effectue aucune synchronisation.

Dans la pratique, vous n'utiliserez presque jamais cela à partir de plusieurs threads en même temps, donc la synchronisation qui le StringBufferfait est presque toujours inutile.

Jesper
la source
3

Personnellement, je ne pense pas que le monde réel puisse être utilisé StringBuffer. Quand voudrais-je jamais communiquer entre plusieurs threads en manipulant une séquence de caractères? Cela ne semble pas du tout utile, mais je n'ai peut-être pas encore vu la lumière :)

fredoverflow
la source
3

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

Mais pourquoi nous avons deux classes dans le même but?

La raison en est que StringBufferThread est sûr et StringBuilderne l'est pas. StringBuilderest une nouvelle classe StringBuffer Apiet elle a été introduite dans JDK5et est toujours recommandée si vous travaillez dans un environnement à thread unique car c'est beaucoupFaster

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

Hitesh Garg
la source
2

En java, String est immuable. Étant immuables, nous voulons dire qu'une fois qu'une chaîne est créée, nous ne pouvons pas changer sa valeur. StringBuffer est modifiable. Une fois qu'un objet StringBuffer est créé, nous ajoutons simplement le contenu à la valeur de l'objet au lieu de créer un nouvel objet. StringBuilder est similaire à StringBuffer mais il n'est pas thread-safe. Les méthodes de StingBuilder ne sont pas synchronisées mais par rapport aux autres chaînes, le Stringbuilder s'exécute le plus rapidement. Vous pouvez apprendre la différence entre String, StringBuilder et StringBuffer en les implémentant.

Rajat Ghai
la source