Home  >  Article  >  Java  >  Detailed analysis of the performance of String, StringBuffer, and StringBuilder classes in Java

Detailed analysis of the performance of String, StringBuffer, and StringBuilder classes in Java

高洛峰
高洛峰Original
2017-01-22 11:25:121411browse

We must first remember the characteristics of the three:

String string constant

StringBuffer string variable (thread-safe)

StringBuilder string variable (non-thread Security)

1. Definition
Looking at the API, you will find that String, StringBuffer, and StringBuilder all implement the CharSequence interface. Although they are all related to strings, their processing mechanisms are different.

String: It is an immutable quantity, that is, it cannot be modified after it is created.

StringBuffer: It is a variable string sequence. Like String, it is an ordered string sequence (array of char type) stored in memory. The difference is that the value of the StringBuffer object is is variable.

StringBuilder: It is basically the same as the StringBuffer class. They are both variable characters and string sequences. The difference is that StringBuffer is thread-safe and StringBuilder is thread-unsafe. In terms of performance, since the operation of the String class is to generate new String objects, and StringBuilder and StringBuffer are just expansions of a character array, the operation of the String class is much slower than StringBuffer and StringBuilder.

2. Usage scenarios
Scenarios for using the String class: The String class can be used in scenarios where strings do not change frequently, such as the declaration of constants and a small number of variable operations.
Scenarios for using the StringBuffer class: If you frequently perform string operations (such as splicing, replacement, deletion, etc.) and run in a multi-threaded environment, you can consider using StringBuffer, such as XML parsing, HTTP parameter parsing and encapsulation.
Scenarios for using the StringBuilder class: If you frequently perform string operations (such as splicing, replacement, and deletion, etc.) and run in a single-threaded environment, you can consider using StringBuilder, such as the assembly of SQL statements, JSON encapsulation, etc. .

3. Analysis
Briefly speaking, the main performance difference between the String type and the StringBuffer type is that String is an immutable object, so every time the String type is changed, it is actually equivalent to generating Create a new String object and then point the pointer to the new String object. Therefore, it is best not to use String for strings that frequently change content, because each time an object is generated, it will have an impact on system performance. Especially when there are too many unreferenced objects in the memory, the JVM's GC will start to work, and the speed will definitely be quite high. slow.

If you use the StringBuffer class, the result will be different. Each result will operate on the StringBuffer object itself, instead of generating a new object and then changing the object reference. So in general we recommend using StringBuffer, especially when string objects change frequently. In some special cases, the string concatenation of String objects is actually interpreted by the JVM as the concatenation of StringBuffer objects, so in these cases the speed of String objects will not be slower than that of StringBuffer objects, and especially the following string objects are generated , String efficiency is much faster than StringBuffer:

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

You will be surprised to find that the speed of generating String S1 objects is simply too fast, and At this time, StringBuffer actually has no advantage at all in terms of speed. In fact, this is a trick of the JVM. In the eyes of the JVM, this

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

is actually:

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

So of course it doesn’t take much time. But what everyone should pay attention to here is that if your string comes from another String object, the speed will not be that fast, for example:

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

This At that time, the JVM will behave in the original way.

4. In-depth JVM optimization processing
Is there really any performance cost above? String splicing is so commonly used, is there no special processing optimization? The answer is yes, this optimization is performed in JVM compilation .java to bytecode.

If a Java program wants to run, it needs to go through two periods, compilation time and runtime. During compilation, Java JVM (Compiler) converts java files into bytecode. At runtime, the Java Virtual Machine (JVM) runs the bytecode generated at compile time. Through these two periods, Java achieved the so-called compilation in one place and run everywhere.

Let's experiment with what optimizations have been done during compilation, and we create a piece of code that may cause performance penalties.

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);
 }
}

Compile Concatenation.java. Get Concatenation.class

javac Concatenation.java

Then we use javap to decompile the compiled Concatenation.class file. javap -c Concatenation. If the javap command is not found, consider adding the directory where javap is located to the environment variable or using the full path to javap.

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
}

Among them, ldc, astore, etc. are java bytecode instructions, similar to assembly instructions. The following comments use Java-related content for explanation. We can see that there are many StringBuilders above, but we do not call them explicitly in the Java code. This is the optimization done by the JavaJVM. When the JavaJVM encounters string splicing, it will create a StringBuilder object. The subsequent splicing, in fact, Is to call the append method of the StringBuilder object. In this way, there will be no problems that we worried about above.

五、仅靠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中文网!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn