Java字符串拼接比较

各种拼接方法的效率比较

import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestString {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Test
    public void testPlus() {
        String s = "";
        long ts = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            s = s + String.valueOf(i);
        }
        long te = System.currentTimeMillis();
        logger.info("+ cost {} ms", te - ts);
    }
    @Test
    public void testConcat() {
        String s = "";
        long ts = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            s = s.concat(String.valueOf(i));
        }
        long te = System.currentTimeMillis();
        logger.info("concat cost {} ms", te - ts);
    }
    @Test
    public void testJoin() {
        List<String> list = new ArrayList<String>();
        long ts = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            list.add(String.valueOf(i));
        }
        StringUtils.join(list, "");
        long te = System.currentTimeMillis();
        logger.info("StringUtils.join cost {} ms", te - ts);
    }
    @Test
    public void testStringBuffer() {
        StringBuffer sb = new StringBuffer();
        long ts = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            sb.append(String.valueOf(i));
        }
        sb.toString();
        long te = System.currentTimeMillis();
        logger.info("StringBuffer cost {} ms", te - ts);
    }
    @Test
    public void testStringBuilder() {
        StringBuilder sb = new StringBuilder();
        long ts = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            sb.append(String.valueOf(i));
        }
        sb.toString();
        long te = System.currentTimeMillis();
        logger.info("StringBuilder cost {} ms", te - ts);
    }
}

运行结果如下:

11:00:22,359 INFO TestString:23 - + cost 1828 ms

11:00:22,921 INFO TestString:34 - concat cost 562 ms

11:00:22,937 INFO TestString:46 - StringUtils.join cost 16 ms

11:00:22,968 INFO TestString:58 - StringBuffer cost 31 ms

11:00:23,031 INFO TestString:70 - StringBuilder cost 63 ms

要特别注意的是:

StringBuilder 循环的次数是其它的10倍,如果是一样,那么返回 0,可见StringBuilder 的速度之快。

总结:

用+的方式效率最差,concat由于是内部机制实现,比+的方式好了不少。

Join 和 StringBuffer,相差不大,Join方式要快些,可见这种JavaScript中快速拼接字符串的方式在Java中也非常适用。

+和concat的字节码中都有用到StringBuilder

StringBuilder 的速度最快,但其有线程安全的问题,而且只有JDK5支持。

Java中仅有的重载运算符

在Java中,唯一被重载的运算符就是字符串的拼接相关的。+,+=。除此之外,Java设计者不允许重载其他的运算符。

+拼接剖析

public class Solution {

     public  static  void  main(String  [ ] args){ 

          String userName = "Andy";
          String age = "24";
          String job = "Developer";
          String info = userName + age + job;
          System.out.println(info);

        }

}

对Concatenation.java进行编译一下。得到Concatenation.class

然后我们使用javap反编译一下编译出来的Concatenation.class文件。javap -c Concatenation。如果没有找到javap命令,请考虑将javap所在目录加入环境变量或者使用javap的完整路径。

Compiled from "Solution.java"
class nowcoder.Solution {
  nowcoder.Solution();
    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.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      20: aload_2
      21: invokevirtual #7                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      24: aload_3
      25: invokevirtual #7                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      28: invokevirtual #8                  // Method java/lang/StringBuilder.to
String:()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.printl
n:(Ljava/lang/String;)V
      41: return
}

其中,ldc,astore等为java字节码的指令,类似汇编指令。后面的注释使用了Java相关的内容进行了说明。 我们可以看到上面有很多StringBuilder,但是我们在Java代码里并没有显示地调用,这就是Java编译器做的优化,当Java编译器遇到字符串拼接的时候,会创建一个StringBuilder对象,后面的拼接,实际上是调用StringBuilder对象的append方法。这样就不会有我们上面担心的问题了。

注意:

当多个确定量(常量)字符串相加时(情况1),编译器直接将它们编辑为相加后的字符串,这样的情况下用“+”比StringBuilder运行时效率更高。

+与StringBuilder

public void  implicitUseStringBuilder(String[] values) {
  String result = "";
  for (int i = 0 ; i < values.length; i ++) {
      result += values[i];
  }
  System.out.println(result);
}

这里面有一个很重要的就是StringBuilder对象创建发生在循环之间,也就是意味着有多少次循环会创建多少个StringBuilder对象,这样明显不好。赤裸裸地低水平代码啊。

稍微优化一下,瞬间提升逼格。

public void explicitUseStringBuider(String[] values) {
  StringBuilder result = new StringBuilder();
  for (int i = 0; i < values.length; i ++) {
      result.append(values[i]);
  }
}

这样不会多次创建StringBuilder

总的来说,我们在循环体中需要尽量避免隐式或者显式创建StringBuilder. 所以那些了解代码如何编译,内部如何执行的人,写的代码档次都比较高。

results matching ""

    No results matching ""