zoukankan      html  css  js  c++  java
  • 【转】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就快多了。特此记下。

  • 相关阅读:
    变量可变性问题
    Android 创建Listener监听器形式选择:匿名内部类?外部类?
    linux下安装zookeeper
    翻页工具类
    将哈夫曼树转化成二叉树
    Activity的启动流程分析
    题目1186:打印日期
    数据库设计--数据流图(DFD)
    c#基础之数组
    10.3.1 一个CONNECT BY的样例
  • 原文地址:https://www.cnblogs.com/Dhouse/p/6899254.html
Copyright © 2011-2022 走看看