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. 所以那些了解代码如何编译,内部如何执行的人,写的代码档次都比较高。

你可能感兴趣的:(java-字符串拼接)