StringBuilder在高性能场景下的正确用法

2016/4/3 posted in  Java

初始长度很重要

StringBuilder的内部有一个char[],不同的append()就是不断的往char[]里填东西的过程。

new StringBuilder()char[]的默认长度为16,超过就用System.arraycopy成倍复制扩容。

这样一来有数组拷贝的成本,二来原来的char[]也白白浪费了,要被GC掉。

所以,合理设置一个初始值是很重要的。

一种长度设置的思路,在append()的时候,不急着往char[]里塞东西,而是先拿一个String[]把它们都存起来,到了最后才把所有Stringlength加起来,构造一个合理长度的StringBuilder

但是,还是会浪费一倍的char[]

因为:

return new String(value, 0, count);

String的构造函数会用System.arraycopy()复制一次传入的char[]来保证安全性及不可变性,这样StringBuilder里的char[]就白白牺牲掉了。

为了不浪费这些char[],可以重用StringBuilder

重用StringBuilder

public StringBuilder getStringBuilder() {
    sb.setLength(0);
    return sb;
}

为了避免并发冲突,这个Holder一般设为ThreadLocal

+和StringBuilder

String str = "hello " + user.getName();

这一句经过javac编译后的效果,的确等价于使用StringBuilder,但没有设定长度。

但是,如果像下面这样:

String str = "hello ";
str = str + user.getName();

每一条语句,都会生成一个新的StringBuilder,这样这里就有了两个StringBuilder,性能就完全不一样了。

保险起见,还是继续自己用StringBuilder并设定好长度。

private static final ThreadLocal<StringBuilderHelper> threadLocalStringBuilderHolder = new ThreadLocal<StringBuilderHelper>() {
    protected StringBuilderHelper initialValue() {
        return new StringBuilderHelper(256);
    }
}

StringBuilder sb = threadLocalStringBuilderHolder.get().resetAndGetStringBuilder();

StringBuidlerHolder

public class StringBuilderHolder {
    private final StringBuilder sb;
    
    public StringBuilderHolder(int capacity) {
        sb = new StringBuidler(capacity);
    }
    
    public StringBuilder resetAndGetStringBuilder() {
        sb.setLength(0);
        return sb;
    }
}

Reference

http://calvin1978.blogcn.com/articles/stringbuilder.html