Heim  >  Artikel  >  Java  >  Detaillierte Analyse der Leistung der Klassen String, StringBuffer und StringBuilder in Java

Detaillierte Analyse der Leistung der Klassen String, StringBuffer und StringBuilder in Java

高洛峰
高洛峰Original
2017-01-22 11:25:121389Durchsuche

Wir müssen uns zunächst an die Eigenschaften der drei erinnern:

String-String-Konstante

StringBuffer-String-Variable (Thread-sicher)

StringBuilder-String-Variable (ohne Thread) Sicherheit)

1. Definition
Wenn Sie sich die API ansehen, werden Sie feststellen, dass String, StringBuffer und StringBuilder alle die CharSequence-Schnittstelle implementieren. Obwohl sie sich alle auf Strings beziehen, sind ihre Verarbeitungsmechanismen unterschiedlich.

String: Es handelt sich um eine unveränderliche Größe, das heißt, sie kann nach ihrer Erstellung nicht mehr geändert werden.

StringBuffer: Es handelt sich um eine variable Zeichenfolgenfolge (Array vom Typ char) im Speicher. Der Unterschied besteht darin, dass der Wert des StringBuffer-Objekts variabel ist.

StringBuilder: Es handelt sich im Grunde um dasselbe wie die StringBuffer-Klasse. Es handelt sich sowohl um variable Zeichen als auch um Zeichenfolgensequenzen. Der Unterschied besteht darin, dass StringBuffer threadsicher und StringBuilder threadunsicher ist. In Bezug auf die Leistung ist die Operation der String-Klasse viel langsamer als die von StringBuffer und StringBuilder, da die Operation der String-Klasse darin besteht, neue String-Objekte zu generieren und StringBuilder und StringBuffer nur Erweiterungen eines Zeichenarrays sind.

2. Verwendungsszenarien
Szenarien für die Verwendung der String-Klasse: Die String-Klasse kann in Szenarien verwendet werden, in denen sich Zeichenfolgen nicht häufig ändern, z. B. bei der Deklaration von Konstanten und einer kleinen Anzahl von Variablenoperationen.
Szenarien für die Verwendung der StringBuffer-Klasse: Wenn Sie häufig Zeichenfolgenoperationen (z. B. Spleißen, Ersetzen, Löschen usw.) ausführen und in einer Multithread-Umgebung ausführen, können Sie die Verwendung von StringBuffer in Betracht ziehen, z. B. XML-Analyse und HTTP-Parameter Analyse und Kapselung.
Szenarien für die Verwendung der StringBuilder-Klasse: Wenn Sie häufig String-Operationen (z. B. Spleißen, Ersetzen und Löschen usw.) ausführen und in einer Single-Thread-Umgebung ausführen, können Sie die Verwendung von StringBuilder in Betracht ziehen, z. B. zum Zusammenstellen von SQL-Anweisungen. JSON-Kapselung usw. .

3. Analyse
Kurz gesagt besteht der Hauptleistungsunterschied zwischen dem String-Typ und dem StringBuffer-Typ darin, dass String ein unveränderliches Objekt ist, sodass jede Änderung des String-Typs tatsächlich einer Generierung entspricht Erstellen Sie ein neues String-Objekt und zeigen Sie dann mit dem Zeiger auf das neue String-Objekt. Daher ist es am besten, String nicht für Zeichenfolgen zu verwenden, deren Inhalt sich häufig ändert, da dies jedes Mal, wenn ein Objekt generiert wird, Auswirkungen auf die Systemleistung hat. Insbesondere wenn zu viele nicht referenzierte Objekte im Speicher vorhanden sind, wird der GC der JVM gestartet zu arbeiten, und die Geschwindigkeit wird definitiv ziemlich hoch sein.

Wenn Sie die StringBuffer-Klasse verwenden, ist das Ergebnis unterschiedlich. Jedes Ergebnis wird auf das StringBuffer-Objekt selbst angewendet, anstatt ein neues Objekt zu generieren und dann die Objektreferenz zu ändern. Daher empfehlen wir im Allgemeinen die Verwendung von StringBuffer, insbesondere wenn sich String-Objekte häufig ändern. In einigen Sonderfällen wird die String-Verkettung von String-Objekten von der JVM tatsächlich als die Verkettung von StringBuffer-Objekten interpretiert, sodass in diesen Fällen die Geschwindigkeit von String-Objekten nicht langsamer ist als die von StringBuffer-Objekten, insbesondere die folgenden String-Objekte generiert, String-Effizienz ist viel schneller als StringBuffer:

String S1 = “This is only a" + “ simple" + “ test";
StringBuffer Sb = new StringBuilder(“This is only a").append(“ simple").append(“ test");

Sie werden überrascht sein, dass die Geschwindigkeit beim Generieren von String-S1-Objekten einfach zu hoch ist. und zu diesem Zeitpunkt hat StringBuffer tatsächlich überhaupt keinen Geschwindigkeitsvorteil. Tatsächlich ist dies ein Trick der JVM. In den Augen der JVM ist dies

String S1 = “This is only a" + “ simple" + “test";

tatsächlich:

String S1 = “This is only a simple test";

Es dauert also natürlich nicht lange. Was hier jedoch jeder beachten sollte, ist, dass die Geschwindigkeit nicht so hoch ist, wenn Ihr String von einem anderen String-Objekt stammt, zum Beispiel:

String S2 = "This is only a";
String S3 = "simple";
String S4 = "test";
String S1 = S2 +S3 + S4;

At Diesmal verhält sich die JVM wie ursprünglich.

4. Detaillierte JVM-Optimierungsverarbeitung
Gibt es wirklich Leistungseinbußen, die so häufig verwendet werden? Gibt es keine spezielle Verarbeitungsoptimierung? Die Antwort lautet: Ja, diese Optimierung wird in JVM durchgeführt Kompilierung von .java in Bytecode.

Wenn ein Java-Programm ausgeführt werden soll, muss es zwei Zeiträume durchlaufen: Kompilierungszeit und Laufzeit. Während der Kompilierung konvertiert Java JVM (Compiler) Java-Dateien in Bytecode. Zur Laufzeit führt die Java Virtual Machine (JVM) den zur Kompilierzeit generierten Bytecode aus. Durch diese beiden Perioden erreichte Java die sogenannte Kompilierung an einem Ort und konnte überall ausgeführt werden.

Lassen Sie uns mit den Optimierungen experimentieren, die während der Kompilierung vorgenommen wurden, und wir erstellen einen Codeabschnitt, der möglicherweise einen Leistungseinbußen mit sich bringt.

public class Concatenation {
 public static void main(String[] args) {
   String userName = "Andy";
   String age = "24";
   String job = "Developer";
   String info = userName + age + job;
   System.out.println(info);
 }
}

Concatenation.java kompilieren. Holen Sie sich Concatenation.class

javac Concatenation.java

Dann verwenden wir Javap, um die kompilierte Concatenation.class-Datei zu dekompilieren. javap -c Verkettung. Wenn der Befehl javap nicht gefunden wird, sollten Sie erwägen, das Verzeichnis, in dem sich javap befindet, zur Umgebungsvariablen hinzuzufügen oder den vollständigen Pfad zu javap zu verwenden.

17:22:04-androidyue~/workspace_adt/strings/src$ javap -c Concatenation
Compiled from "Concatenation.java"
public class Concatenation {
 public Concatenation();
  Code:
    0: aload_0
    1: invokespecial #1         // Method java/lang/Object."<init>":()V
    4: return   
 
 public static void main(java.lang.String[]);
  Code:
    0: ldc      #2         // String Andy
    2: astore_1
    3: ldc      #3         // String 24
    5: astore_2
    6: ldc      #4         // String Developer
    8: astore_3
    9: new      #5         // class java/lang/StringBuilder
   12: dup
   13: invokespecial #6         // Method java/lang/StringBuilder."<init>":()V
   16: aload_1
   17: invokevirtual #7         // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   20: aload_2
   21: invokevirtual #7         // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   24: aload_3
   25: invokevirtual #7         // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   28: invokevirtual #8         // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   31: astore    4
   33: getstatic   #9         // Field java/lang/System.out:Ljava/io/PrintStream;
   36: aload     4
   38: invokevirtual #10         // Method java/io/PrintStream.println:(Ljava/lang/String;)V
   41: return
}

Darunter sind LDC, Astore usw. Java-Bytecode-Anweisungen, ähnlich wie Montageanweisungen. Die folgenden Kommentare verwenden zur Erläuterung Java-bezogene Inhalte. Wir können sehen, dass es oben viele StringBuilder gibt, aber wir rufen sie nicht explizit im Java-Code auf. Dies ist die von der JavaJVM durchgeführte Optimierung, wenn sie auf String-Splicing stößt Tatsache ist, die Append-Methode des StringBuilder-Objekts aufzurufen. Auf diese Weise wird es keine Probleme geben, über die wir uns oben Sorgen gemacht haben.

五、仅靠JVM优化?
既然JVM帮我们做了优化,是不是仅仅依靠JVM的优化就够了呢,当然不是。 
下面我们看一段未优化性能较低的代码

public void implicitUseStringBuilder(String[] values) {
 String result = "";
 for (int i = 0 ; i < values.length; i ++) {
   result += values[i];
 }
 System.out.println(result);
}

   

使用javac编译,使用javap查看

public void implicitUseStringBuilder(java.lang.String[]);
  Code:
    0: ldc      #11         // String
    2: astore_2
    3: iconst_0
    4: istore_3
    5: iload_3
    6: aload_1
    7: arraylength
    8: if_icmpge   38
   11: new      #5         // class java/lang/StringBuilder
   14: dup
   15: invokespecial #6         // Method java/lang/StringBuilder."<init>":()V
   18: aload_2
   19: invokevirtual #7         // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   22: aload_1
   23: iload_3
   24: aaload
   25: invokevirtual #7         // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   28: invokevirtual #8         // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   31: astore_2
   32: iinc     3, 1
   35: goto     5
   38: getstatic   #9         // Field java/lang/System.out:Ljava/io/PrintStream;
   41: aload_2
   42: invokevirtual #10         // Method java/io/PrintStream.println:(Ljava/lang/String;)V
   45: return

   

其中8: if_icmpge 38 和35: goto 5构成了一个循环。8: if_icmpge 38的意思是如果JVM操作数栈的整数对比大于等于(i 7db7972854f88856e0b26158d61ce2d3 String

Java.lang.StringBuffer是线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。在程序中可将字符串缓冲区安全地用于多线程。而且在必要时可以对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。

StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。

例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append(“le”) 会使字符串缓冲区包含“startle”(累加);而 z.insert(4, “le”) 将更改字符串缓冲区,使之包含“starlet”。

在大部分情况下 StringBuilder > StringBuffer

java.lang.StringBuilder一个可变的字符序列是JAVA 5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步,所以使用场景是单线程。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的使用方法基本相同。

更多详细分析Java中String、StringBuffer、StringBuilder类的性能相关文章请关注PHP中文网!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn