【转】String字符串相加的问题

String字符串相加的问题

 

 

 

前几天同事跟我说我之前写的代码中在操作字符串时候,使用字符串相加的方式而不是使用StringBuffer或者StringBuilder导致内存开销很大。这个问题一直在困扰我,因为在《Think in java》一书中,作者说使用“+”拼接字符串并不比StringBuffer或者StringBuilder效率低下,因为“+”是java唯一一个系统级的针对字符串的重载过的操作符。

大家都知道String是一个final修饰的类。那么两个字符串使用“+”相加到底会不会导致字符串操作效率受到影响呢?

 

 

下面是书中和牛人的理解的结合:

用bytecode来说明问题:

1.使用String + 的方式:
public class Test2 {
    public static void main(String[] args) {
        String a = "a";
        String b = "b";
        String c = "c";
        String s = a + b + c;
    }
}

 

【同时揭示了为何变量相加时 与常量池中的“abc”不相等  因为相加变量本质上是一个StringBuilder对象】

对应的bytecode为:

public class Test2 extends java.lang.Object{
public Test2();
  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 a
   2:   astore_1
   3:   ldc     #3; //String b
   5:   astore_2
   6:   ldc     #4; //String c
   8:   astore_3
   9:   new     #5; //class StringBuffer
   12:  dup
   13:  invokespecial   #6; //Method java/lang/StringBuffer."<init>":()V
   16:  aload_1
   17:  invokevirtual   #7; //Method java/lang/StringBuffer.append:(Ljava/lang/S
tring;)Ljava/lang/StringBuffer;
   20:  aload_2
   21:  invokevirtual   #7; //Method java/lang/StringBuffer.append:(Ljava/lang/S
tring;)Ljava/lang/StringBuffer;
   24:  aload_3
   25:  invokevirtual   #7; //Method java/lang/StringBuffer.append:(Ljava/lang/S
tring;)Ljava/lang/StringBuffer;
   28:  invokevirtual   #8; //Method java/lang/StringBuffer.toString:()Ljava/lan
g/String;
   31:  astore  4
   33:  return

}

2.使用StringBuffer.append()的方式:
public class Test3 {
    public static void main(String[] args) {
        String a = "a";
        String b = "b";
        String c = "c";
        StringBuffer sb = new StringBuffer();
        sb.append(a).append(b).append(c);
        String s = sb.toString();
    }
}

对应的bytecode为:

public class Test3 extends java.lang.Object{
public Test3();
  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 a
   2:   astore_1
   3:   ldc     #3; //String b
   5:   astore_2
   6:   ldc     #4; //String c
   8:   astore_3
   9:   new     #5; //class StringBuffer
   12:  dup
   13:  invokespecial   #6; //Method java/lang/StringBuffer."<init>":()V
   16:  astore  4
   18:  aload   4
   20:  aload_1
   21:  invokevirtual   #7; //Method java/lang/StringBuffer.append:(Ljava/lang/S
tring;)Ljava/lang/StringBuffer;
   24:  aload_2
   25:  invokevirtual   #7; //Method java/lang/StringBuffer.append:(Ljava/lang/S
tring;)Ljava/lang/StringBuffer;
   28:  aload_3
   29:  invokevirtual   #7; //Method java/lang/StringBuffer.append:(Ljava/lang/S
tring;)Ljava/lang/StringBuffer;
   32:  pop
   33:  aload   4
   35:  invokevirtual   #8; //Method java/lang/StringBuffer.toString:()Ljava/lan
g/String;
   38:  astore  5
   40:  return

}

看到了么?后者不光不比前者效率高,反而多了一些临时变量的存取

 

 

至于推荐用StringBuffer来代替String+拼装字符串,说的是循环方式下,如:

public class Test4 {
    public static void main(String[] args) {
        String s = "s";
        for (int i = 0; i < 20; i++) {
            s += i;
        }
    }
}

对应的bytecode为:

public class Test4 extends java.lang.Object{
public Test4();
  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 s
   2:   astore_1
   3:   iconst_0
   4:   istore_2
   5:   iload_2
   6:   bipush  20
   8:   if_icmpge       36
   11:  new     #3; //class StringBuffer
   14:  dup
   15:  invokespecial   #4; //Method java/lang/StringBuffer."<init>":()V
   18:  aload_1
   19:  invokevirtual   #5; //Method java/lang/StringBuffer.append:(Ljava/lang/S
tring;)Ljava/lang/StringBuffer;
   22:  iload_2
   23:  invokevirtual   #6; //Method java/lang/StringBuffer.append:(I)Ljava/lang
/StringBuffer;
   26:  invokevirtual   #7; //Method java/lang/StringBuffer.toString:()Ljava/lan
g/String;
   29:  astore_1
   30:  iinc    2, 1
   33:  goto    5
   36:  return

}

如果用String+的方式,每循环一次,就会重新new一个StringBuffer对象,这样的内存消耗完全是不必要的(在数据量大的情况下,还会导致内存不足的错误),所以要这样做:

public class Test5 {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("s");
        for (int i = 0; i < 20; i++) {
            sb.append(i);
        }
    }
}
这样无论循环多少次,都只会生成一个StringBuffer对象

 

 

 

 

回想下代码,我确实是在循环中使用了“+”,而且循环次数还不少,同事说的没错,改为StringBuffer就快多了。特此记下。

posted @ 2017-05-24 15:40  Mr.Ming2  阅读(1010)  评论(0编辑  收藏  举报