用StringBuilder(StringBuffer)#append替代字符串”+”会带来性能提升吗(一)

2014-11-23 23:19:18 · 作者: · 浏览: 0

经常看到一些论坛在谈java代码优化的时候讲到要将字符串连接操作”+”换成StringBuilder(或StringBuffer,后面为简单起见,只说StringBuilder)的append操作以提升性能,那么字符串连接使用StringBuilder#append来替代”+”真的会带来性能提升吗?

不忙回答,先看几个例子,代码如下:

public class StringConcat { public static void main(String... args) { concat1(); concat2(); concat3(); } public static void concat1() { String s = "today is " + "a good day"; System.out.println(s); } public static void concat2() { int count = 2; String tmp = " on the desk"; String s2 = "there are " + count + " books " + tmp; System.out.println(s2); } public static void concat3() { String s3 = ""; for(int i=0; i<100; i++) { s3 = s3 + i; } System.out.println(s3); } }

接下来分别分析下这三个操作字符串的方法,通过javap命令反编译.class文件:javap -c StringConcat ,获得字节码指令如下(只摘取concat1,concat2,concat3三个方法的):

public static void concat1(); Code: 0: ldc #5; //String today is a good day 2: astore_0 3: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream; 6: aload_0 7: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 10: return public static void concat2(); Code: 0: iconst_2 1: istore_0 2: ldc #8; //String on the desk 4: astore_1 5: new #9; //class java/lang/StringBuilder 8: dup 9: invokespecial #10; //Method java/lang/StringBuilder." ":()V 12: ldc #11; //String there are 14: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 17: iload_0 18: invokevirtual #13; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 21: ldc #14; //String books 23: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 26: aload_1 27: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 30: invokevirtual #15; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 33: astore_2 34: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream; 37: aload_2 38: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 41: return public static void concat3(); Code: 0: ldc #16; //String 2: astore_0 3: iconst_0 4: istore_1 5: iload_1 6: bipush 100 8: if_icmpge 36 11: new #9; //class java/lang/StringBuilder 14: dup 15: invokespecial #10; //Method java/lang/StringBuilder." ":()V 18: aload_0 19: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 22: iload_1 23: invokevirtual #13; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 26: invokevirtual #15; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 29: astore_0 30: iinc 1, 1 33: goto 5 36: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream; 39: aload_0 40: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V

来分析下三个方法的字节码含义

在concat1中,是两个字面值(字符串常量)的连接,从concat1的字节码的第0条(0: ldc #5; //String today is a good day)可以看到,该方法直接从常量池加载”String today is a good day”,也就是说,String s = “today is ” + “a good day”;这条语句在编译后已经变成了一个字符串,等效于String s = “String today is a good day”,运行期间根本无需做连接操作了,所以对于字符串字面值的连接,使用StringBuilder是没有任何意义的。

在concat2中,是变量参与字符串的连接。从反编译的字节码中可以看出,编译期间已经转换成了StringBuilder的append操作,

String s2 = "there are " + count + " books " + tmp;

语句在编译之后已经等效于(即[5,30]之间的指令):

String s2 = new StringBuilder().append("there are ").append(count).append(" books").append(tmp).toString();

由此可见,在这样的字符串连接代码里显式