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 < values.length的相反结果)成立,则跳到第38行(System.out)。35: goto 5则表示直接跳到第5行。
但是这里面有一个很重要的就是StringBuilder对象创建发生在循环之间,也就是意味着有多少次循环会创建多少个StringBuilder对象,这样明显不好。赤裸裸地低水平代码啊。
稍微优化一下,瞬间提升逼格。
public void explicitUseStringBuider(String[] values) { StringBuilder result = new StringBuilder(); for (int i = 0; i < values.length; i ++) { result.append(values[i]); } }
对应的编译后的信息
public void explicitUseStringBuider(java.lang.String[]); Code: 0: new #5 // class java/lang/StringBuilder 3: dup 4: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V 7: astore_2 8: iconst_0 9: istore_3 10: iload_3 11: aload_1 12: arraylength 13: if_icmpge 30 16: aload_2 17: aload_1 18: iload_3 19: aaload 20: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 23: pop 24: iinc 3, 1 27: goto 10 30: return
从上面可以看出,13: if_icmpge 30和27: goto 10构成了一个loop循环,而0: new #5位于循环之外,所以不会多次创建StringBuilder.
总的来说,我们在循环体中需要尽量避免隐式或者显式创建StringBuilder. 所以那些了解代码如何编译,内部如何执行的人,写的代码档次都比较高。
六、结论
在大部分情况下 StringBuffer > 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中文网!