String, StringBuffer und StringBuilder

213

Bitte sagen Sie mir , eine Echtzeit - Situation zu vergleichen String, StringBufferund StringBuilder?

JavaUser
quelle

Antworten:

378

Mutabilitätsunterschied:

Stringist unveränderlich , wenn Sie versuchen , ihre Werte zu ändern, wird eine andere Aufgabe erstellt, während StringBufferund StringBuildersind wandelbar , so können sie ihre Werte ändern.

Thread-Sicherheitsunterschied:

Der Unterschied zwischen StringBufferund StringBuilderist, dass StringBufferes threadsicher ist. Wenn die Anwendung nur in einem einzigen Thread ausgeführt werden muss, ist es besser, sie zu verwenden StringBuilder. StringBuilderist effizienter als StringBuffer.

Situationen:

  • Wenn sich Ihre Zeichenfolge nicht ändern wird, verwenden Sie eine Zeichenfolgenklasse, da ein StringObjekt unveränderlich ist.
  • Wenn sich Ihre Zeichenfolge ändern kann (Beispiel: viele Logik und Operationen bei der Erstellung der Zeichenfolge) und nur von einem einzelnen Thread aus zugegriffen werden kann, ist die Verwendung von a StringBuilderausreichend.
  • Wenn sich Ihre Zeichenfolge ändern kann und von mehreren Threads aus darauf zugegriffen wird, verwenden Sie a, StringBufferda StringBufferes synchron ist, damit Sie Thread-Sicherheit haben.
Bakkal
quelle
16
Außerdem ist die Verwendung von String für Logikoperationen ziemlich langsam und wird überhaupt nicht empfohlen, da die JVM den String im Bytecode in einen StringBuffer konvertiert. Es wird viel Aufwand verschwendet, von String zu StringBuffer und dann wieder zurück zu String zu konvertieren.
Pieter van Niekerk
2
Also , Stringswenn wir den Wert ein anderes Objekt verändern wird erstellt. Wird die alte Objektreferenz ungültig gemacht, so dass es sich um Müll handelt, der von der gesammelt wurde, GCoder wird sie sogar durch Müll gesammelt?
Harte Vardhan
@PietervanNiekerk Was meinst du mit logischen Operationen?
Emlai
Ich meine, logische Operationen sind grundlegende String-Operationen. Nun möchte ich eine Frage stellen, wie von @Peter angegeben. Sollten wir in allen Fällen StringBuffer anstelle von String verwenden, oder gibt es bestimmte Fälle?
JavaDragon
@bakkal Kann ich alle Methoden von Stringwith anwenden StringBuilder?
Roottraveller
47
  • Sie verwenden, Stringwenn eine unveränderliche Struktur angemessen ist. Das Abrufen einer neuen Zeichenfolge von a Stringkann zu einer inakzeptablen Leistungseinbußen führen, entweder in Bezug auf die CPU-Zeit oder den Arbeitsspeicher (das Abrufen von Teilzeichenfolgen ist CPU-effizient, da die Daten nicht kopiert werden, dies bedeutet jedoch, dass möglicherweise eine viel größere Datenmenge zugewiesen bleibt).
  • Sie verwenden diese StringBuilderOption, wenn Sie eine veränderbare Zeichenfolge erstellen müssen, um normalerweise mehrere Zeichenfolgen miteinander zu verknüpfen.
  • Sie verwenden StringBufferunter den gleichen Umständen, die Sie verwenden würden StringBuilder, aber wenn Änderungen an der zugrunde liegenden Zeichenfolge synchronisiert werden müssen (da mehrere Threads den Zeichenfolgenpuffer lesen / ändern).

Sehen Sie ein Beispiel hier .

Artefakt
quelle
2
Prägnant, aber unvollständig, fehlt der grundlegende Grund für die Verwendung von StringBuilder / Buffer. Dies besteht darin, die Neuzuweisung und Array-Kopie des regulären String-Verkettungsverhaltens zu reduzieren oder zu eliminieren.
1
"Sie verwenden String, wenn Sie mit unveränderlichen Strings arbeiten" - macht keinen Sinn. Instanzen von Strings sind unveränderlich, daher sollte der Kommentar möglicherweise "String verwenden, wenn die Speichernutzung aufgrund von Unveränderlichkeit keine Rolle spielt" lauten. Die akzeptierte Antwort deckt die Grundlagen ziemlich gut ab.
fooMonster
28

Die Grundlagen:

Stringist eine unveränderliche Klasse, sie kann nicht geändert werden. StringBuilderist eine veränderbare Klasse, an die Zeichen angehängt, ersetzt oder entfernt und schließlich in eine konvertierte String StringBufferursprüngliche Version von konvertiert werden könnenStringBuilder

Sie sollten es StringBuilderin allen Fällen vorziehen, in denen nur ein einziger Thread auf Ihr Objekt zugreift.

Die Details:

Beachten Sie auch, dass dies StringBuilder/Bufferskeine Zauberei ist, sondern lediglich ein Array als Hintergrundobjekt verwendet und dass das Array neu zugewiesen werden muss, wenn es voll wird. Stellen Sie sicher, dass Ihre StringBuilder/BufferObjekte ursprünglich groß genug sind, damit sie nicht jedes Mal, wenn sie .append()aufgerufen werden, ständig in der Größe geändert werden müssen.

Die Größenänderung kann sehr degeneriert werden. Grundsätzlich wird die Größe des Backing-Arrays bei jeder Erweiterung auf das Zweifache seiner aktuellen Größe geändert. Dies kann dazu führen, dass große Mengen an RAM zugewiesen und nicht verwendet werden, wenn StringBuilder/BufferKlassen größer werden.

In Java String x = "A" + "B";verwendet man einen StringBuilderBlick hinter die Kulissen. In einfachen Fällen gibt es also keinen Vorteil, wenn Sie Ihre eigenen deklarieren. Wenn Sie jedoch Objekte Stringerstellen, die groß sind, z. B. weniger als 4 KB, ist das Deklarieren StringBuilder sb = StringBuilder(4096);viel effizienter als die Verkettung oder die Verwendung des Standardkonstruktors mit nur 16 Zeichen. Wenn Ihr StringWert unter 10.000 liegt, initialisieren Sie ihn aus Sicherheitsgründen mit dem Konstruktor auf 10.000. Wenn es jedoch auf 10k initialisiert ist, schreiben Sie 1 Zeichen mehr als 10k, wird es neu zugewiesen und in ein 20k-Array kopiert. Hoch zu initialisieren ist also besser als zu niedrig.

Im Fall der automatischen Größenänderung wird das Hintergrundarray beim 17. Zeichen neu zugewiesen und auf 32 Zeichen kopiert. Beim 33. Zeichen geschieht dies erneut, und Sie können das Array neu zuweisen und in 64 Zeichen kopieren. Sie können sehen, wie dies zu vielen Neuzuweisungen und Kopien führt, was Sie wirklich zu vermeiden versuchen StringBuilder/Buffer.

Dies ist aus dem JDK 6-Quellcode für 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);
    }

Eine bewährte Methode besteht darin, das StringBuilder/Bufferetwas größere zu initialisieren, als Sie denken, dass Sie es benötigen werden, wenn Sie nicht Stringsofort wissen, wie groß das sein wird, aber Sie können raten. Eine Zuweisung von etwas mehr Speicher als Sie benötigen, ist besser als viele Neuzuweisungen und Kopien.

Achten Sie auch darauf, a StringBuilder/Buffermit a zu initialisieren String, da dies nur die Größe des Strings + 16 Zeichen zuweist. In den meisten Fällen wird nur die entartete Neuzuweisung und der Kopierzyklus gestartet, die Sie vermeiden möchten. Das Folgende stammt direkt aus dem Java 6-Quellcode.

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

Wenn Sie zufällig eine Instanz haben StringBuilder/Buffer, die Sie nicht erstellt haben und den aufgerufenen Konstruktor nicht steuern können, können Sie das entartete Verhalten beim erneuten Zuweisen und Kopieren vermeiden. Rufen Sie .ensureCapacity()mit der gewünschten Größe an, um sicherzustellen, dass das Ergebnis Stringpasst.

Die Alternativen:

Nur als Anmerkung, wenn Sie wirklich tun schwere String Gebäude und Manipulation, gibt es eine viel leistungsorientierte Alternative genannt Seile .

Eine andere Alternative besteht darin, eine StringListImplementierung durch Unterklassifizierung zu erstellen ArrayList<String>und Zähler hinzuzufügen, um die Anzahl der Zeichen bei jeder .append()und anderen Mutationsoperationen der Liste zu verfolgen. Anschließend werden sie überschrieben .toString(), um eine StringBuilderder genau benötigten Größen zu erstellen und die Liste zu durchlaufen und zu erstellen Bei der Ausgabe können Sie sogar StringBuildereine Instanzvariable erstellen und die Ergebnisse von "zwischenspeichern" .toString()und müssen sie nur neu generieren, wenn sich etwas ändert.

Vergessen Sie auch nicht, String.format()wenn Sie eine fest formatierte Ausgabe erstellen, die vom Compiler optimiert werden kann, wenn sie besser wird.

Benutzer177800
quelle
1
Hat String x = "A" + "B";kompilieren wirklich ein String zu sein? Warum sollte es nicht einfach kompiliert werden String x = "AB";, es sollte nur einen StringBuilder verwenden, wenn die Komponenten zur Kompilierungszeit nicht bekannt sind.
Matt Greer
Es könnte die String-Konstanten optimieren, ich kann mich nicht erinnern, wann der Byte-Code das letzte Mal dekompiliert wurde, aber ich weiß, dass wenn dort Variablen vorhanden sind, mit Sicherheit eine StringBuilder-Implementierung verwendet wird. Sie können den JDK-Quellcode herunterladen und selbst herausfinden. "A" + "B" ist mit Sicherheit ein erfundenes Beispiel.
Ich habe mich über String.format () gewundert. Ich habe noch nie wirklich gesehen, dass es in Projekten verwendet wird. Normalerweise ist es der StringBuilder. Nun, normalerweise ist es tatsächlich "A" + "B" + "C", weil die Leute faul sind;) Ich habe immer einen StringBuilder verwendet, auch wenn nur zwei Strings verkettet wurden, weil in Zukunft vielleicht mehr Strings angehängt würden . Ich habe String.format () nie verwendet, hauptsächlich, weil ich mich nie daran erinnert habe, welches JDK eingeführt wurde. Ich sehe, es ist JDK1.5, ich würde es zugunsten der anderen Optionen verwenden.
Jamie Barrow
9

Meinen Sie zur Verkettung?

Beispiel aus der realen Welt: Sie möchten aus vielen anderen eine neue Zeichenfolge erstellen .

Zum Beispiel, um eine Nachricht zu senden:

String

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()

Oder

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

StringBuffer (die Syntax ist genau wie bei StringBuilder, die Effekte unterscheiden sich)

Über

StringBuffer vs. StringBuilder

Ersteres ist synchronisiert und später nicht.

Wenn Sie es also mehrmals in einem einzelnen Thread aufrufen (was 90% der Fälle entspricht), StringBuilderwird es viel schneller ausgeführt, da es nicht aufhört zu sehen, ob es die Thread-Sperre besitzt.

Daher ist die Verwendung empfehlenswert StringBuilder(es sei denn, Sie haben natürlich mehr als einen Thread gleichzeitig, der darauf zugreift, was selten vorkommt).

StringDie Verkettung ( mit dem Operator + ) kann vom Compiler so optimiert werden StringBuilder, dass sie darunter verwendet wird. In den älteren Tagen von Java war dies also kein Grund mehr, sich Sorgen zu machen. Dies war etwas, von dem jeder sagt, dass es um jeden Preis vermieden werden sollte, da jede Verkettung hat ein neues String-Objekt erstellt. Moderne Compiler tun dies nicht mehr, aber es ist dennoch eine gute Praxis, sie StringBuilderstattdessen zu verwenden, nur für den Fall, dass Sie einen "alten" Compiler verwenden.

bearbeiten

Nur für diejenigen, die neugierig sind, macht der Compiler Folgendes für diese Klasse:

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

}

Die Zeilen mit den Nummern 5 bis 27 stehen für die Zeichenfolge mit dem Namen "Literal".

Die Zeilen mit den Nummern 31-53 stehen für den String "builder".

Es ist kein Unterschied, für beide Zeichenfolgen wird genau der gleiche Code ausgeführt.

OscarRyz
quelle
2
Das ist ein wirklich schlechtes Beispiel. Das Initialisieren des StringBuilder mit "Dear" bedeutet, dass das erste .append () eine Neuzuweisung und Kopie bewirkt. Negative Verneinung jeglicher Effizienz, die Sie gegenüber der "normalen" Verkettung erzielen möchten. Ein besseres Beispiel wäre, es mit einer Anfangsgröße zu erstellen, die den gesamten Inhalt des endgültigen Strings enthält.
1
Es ist im Allgemeinen NICHT empfehlenswert, eine StringBuilderString-Verkettung auf der rechten Seite der Zuweisung zu verwenden. Jede gute Implementierung verwendet einen StringBuilder hinter den Kulissen, wie Sie sagen. Darüber hinaus "a" + "b"würde Ihr Beispiel von in ein einziges Literal kompiliert, "ab"aber wenn Sie es verwenden StringBuilder, würde es zu zwei unnötigen Aufrufen von führen append().
Mark Peters
@Mark Ich wollte nicht verwenden, "a"+"b"sondern sagen, was eine String-Verkettung war. Ich ändere sie so, dass sie explizit ist. Was Sie nicht sagen, ist, warum es keine gute Praxis ist, dies zu tun. Genau das macht (ein moderner) Compiler. @fuzzy, ich stimme zu, besonders wenn Sie wissen, wie groß die endgültige Zeichenfolge sein würde (ca.).
OscarRyz
1
Ich denke nicht, dass es eine besonders schlechte Praxis ist, aber ich würde auf keinen Fall eine Empfehlung dafür wünschen. Es ist klobig und schwer zu lesen und übermäßig ausführlich. Außerdem werden Fehler wie Ihre gefördert, bei denen Sie zwei Literale auflösen, die andernfalls als eines kompiliert werden könnten. Nur wenn mir die Profilerstellung sagen würde, dass es einen Unterschied macht, würde ich es auf Ihre Weise tun.
Mark Peters
@ Mark Verstanden. Ich dachte mehr an den "großen Teil des Codes wie eine Vorlage" und nicht wirklich an jedes reguläre String-Literal. Aber ja, ich stimme zu, da sie heutzutage dasselbe tun, macht es keinen Sinn (vor 10 Jahren war ein Grund, eine Änderung in einer Code-Revision abzulehnen) :)
OscarRyz
8
-------------------------------------------------- --------------------------------
                  String StringBuffer StringBuilder
-------------------------------------------------- --------------------------------                 
Lagerbereich | Constant String Pool Heap Heap
Änderbar | Nein (unveränderlich) Ja (veränderlich) Ja (veränderlich)
Thread Safe | Ja, ja, nein
 Leistung | Schnell Sehr langsam Schnell
-------------------------------------------------- --------------------------------
Abhijit Maity
quelle
Warum ist die String-Leistung schnell und die StringBuffer-Leistung sehr langsam?
Gaurav
1
@gaurav Sie können seinen Quellcode lesen, alle Methoden in StringBuffer sind synchronisedund das ist der Grund .
Gehört
8

String-Familie

String

Das String classsteht für Zeichenketten. Alle Zeichenfolgenliterale in Java-Programmen, wie "abc"sie als Instanzen dieser Klasse implementiert sind.

String-Objekte sind nach ihrer Erstellung unveränderlich. Wir können sie nicht ändern. ( Strings sind Konstanten )

  • Wenn ein String verwendet Konstruktor oder eine Methode erstellt , dann werden diese Strings in gespeichert werden Heap - Speicher sowie SringConstantPool. Vor dem Speichern im Pool wird jedoch die intern()Methode aufgerufen , um die Objektverfügbarkeit mit demselben Inhalt im Pool mithilfe der Methode equals zu überprüfen. Wenn String-copy im Pool verfügbar ist, wird die Referenz zurückgegeben. Andernfalls wird das String-Objekt zum Pool hinzugefügt und gibt die Referenz zurück.

    • Die Java-Sprache bietet spezielle Unterstützung für den String-Verkettungsoperator ( +) und für die Konvertierung anderer Objekte in Strings. Die Verkettung von Zeichenfolgen wird über die Klasse StringBuilder (oder StringBuffer) und ihre Append-Methode implementiert.

    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);
    }
  • String-Literale werden in gespeichert StringConstantPool.

    String onlyPool = "Yash";

StringBuilder und StringBuffer sind veränderbare Zeichenfolgen. Das heißt, man kann den Wert dieser Objekte ändern. StringBuffer hat die gleichen Methoden wie der StringBuilder, aber jede Methode in StringBuffer ist synchronisiert, sodass sie threadsicher ist.

  • StringBuffer- und StringBuilder-Daten können nur mit einem neuen Operator erstellt werden. Sie werden also im Heap-Speicher gespeichert.

  • Instanzen von StringBuilder können nicht von mehreren Threads verwendet werden. Wenn eine solche Synchronisation erforderlich ist, wird empfohlen, StringBuffer zu verwenden.

    StringBuffer threadSafe = new StringBuffer("Yash");
    threadSafe.append(".M");
    threadSafe.toString();
    
    StringBuilder nonSync = new StringBuilder("Yash");
    nonSync.append(".M");
    nonSync.toString();
  • StringBuffer und StringBuilder haben spezielle Methoden wie., replace(int start, int end, String str)Und reverse().

    HINWEIS : StringBuffer und SringBuilder können geändert werden, da sie die Implementierung von bereitstellen Appendable Interface.


Wann welches zu verwenden ist.

  • Wenn Sie den Wert nicht jedes Mal ändern möchten, ist es besser, ihn zu verwenden String Class. Wenn Sie als Teil von Generics Comparable<T>Werte sortieren oder vergleichen möchten, wählen Sie 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);
  • Wenn Sie den Wert jedes Mal ändern möchten, wählen Sie StringBuilder, der schneller als StringBuffer ist. Wenn mehrere Threads den Wert ändern, wählen Sie StringBuffer.

Yash
quelle
4

Auch StringBufferist threadsicher, was StringBuildernicht ist.

In einer Echtzeitsituation, in der verschiedene Threads darauf zugreifen, StringBuilderkann dies zu einem unbestimmten Ergebnis führen.

Lars Andren
quelle
3

Beachten Sie, dass Sie StringBuilderanstelle von Java 5 oder neuer anstelle von verwenden sollten StringBuffer. Aus der API-Dokumentation:

Ab Release JDK 5 wurde diese Klasse durch eine entsprechende Klasse ergänzt, die für die Verwendung durch einen einzelnen Thread entwickelt wurde StringBuilder. Die StringBuilderKlasse sollte im Allgemeinen dieser vorgezogen werden, da sie alle gleichen Operationen unterstützt, jedoch schneller ist, da keine Synchronisation durchgeführt wird.

In der Praxis werden Sie dies fast nie von mehreren Threads gleichzeitig verwenden, sodass die Synchronisierung StringBufferfast immer unnötigen Aufwand bedeutet.

Jesper
quelle
3

Persönlich glaube ich nicht, dass es eine reale Verwendung für gibt StringBuffer. Wann würde ich jemals zwischen mehreren Threads kommunizieren wollen, indem ich eine Zeichenfolge manipuliere? Das klingt überhaupt nicht nützlich, aber vielleicht muss ich das Licht noch sehen :)

Fredoverflow
quelle
3

Der Unterschied zwischen String und den beiden anderen Klassen besteht darin, dass String unveränderlich ist und die anderen beiden Klassen veränderbare Klassen sind.

Aber warum haben wir zwei Klassen für den gleichen Zweck?

Grund dafür ist, dass StringBufferThread-sicher ist und StringBuildernicht. StringBuilderist eine neue Klasse StringBuffer Apiund wurde in eingeführt JDK5und wird immer empfohlen, wenn Sie in einer Single-Thread-Umgebung arbeiten, da es viel istFaster

Ausführliche Informationen finden Sie unter http://www.codingeek.com/java/stringbuilder-and-stringbuffer-a-way-to-create-mutable-strings-in-java/

Hitesh Garg
quelle
2

In Java ist String unveränderlich. Unveränderlich zu sein bedeutet, dass wir nach dem Erstellen eines Strings seinen Wert nicht mehr ändern können. StringBuffer ist veränderbar. Sobald ein StringBuffer-Objekt erstellt wurde, hängen wir den Inhalt einfach an den Wert des Objekts an, anstatt ein neues Objekt zu erstellen. StringBuilder ähnelt StringBuffer, ist jedoch nicht threadsicher. Die Methoden von StingBuilder sind nicht synchronisiert, aber im Vergleich zu anderen Strings läuft der Stringbuilder am schnellsten. Sie können den Unterschied zwischen String, StringBuilder und StringBuffer lernen , indem Sie sie implementieren.

Rajat Ghai
quelle