String、SrtringBuffer和StringBuilder的区别
String | StringBuffer | StringBuilder |
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间 | StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量 | 可变类,速度更快 |
不可变 | 可变 | 可变 |
线程安全 | 线程不安全 | |
多线程操作字符串 | 单线程操作字符串 |
关于StringBuilder、StringBuilder、String的使用我们一般知道几点:
1、字符串拼接要用StringBuilder,不要用+,性能就是最好;
2. StringBuilder不是线程安全的,多线程场景下还是要用StringBuffer;
3. 永远不要自己拼接日志信息的字符串,交给slf4j来。
1、设置好初始长度
StringBuilder的内部有一个char[],new StringBuilder() 时char[]的默认长度是16,如果长度超过16就会用System.arraycopy扩容,和集合一样合理的设置初始大小可以减少GC
2、重用StringBuilder
BigDecimal中有一段源码
// Accessors.
StringBuilder getStringBuilder() {
sb.setLength(0);
return sb;
}
StringBuilder.setLength()函数只重置它的count指针,而char[]则会继续重用,而toString()时会把当前的count指针也作为参数传给String的构造函数,所以不用担心把超过新内容大小的旧内容也传进去了。
3、+ 与 StringBuilder
String s = "hello"+ user.getName();
//这一句经过javac编译后的效果,的确等价于使用StringBuilder,但没有设定长度。
String s = new StringBuilder().append(“hello”).append(user.getName());
如果代码之间隔了其他语句,就生成两个StringBuilder
String s = "hello";
// 隔了其他一些语句
s = s + user.getName();
如果放在循环体里面就更多了,效率更低了。
总结
高性能的场景下要考虑用一个ThreadLocal 可重用的StringBuilder。而且重用之后,就不用考虑长度问题了。如果字符串只有一百几十字节,设好初始值没问题。
StringBuilderUtil.java
Threadlocal是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据
public class StringBuilderUtil {
private static final ThreadLocal<StringBuilderHelper> threadLocalStringBuilderHelper = new ThreadLocal<StringBuilderHelper>() {
@Override
protected StringBuilderHelper initialValue() {
return new StringBuilderHelper();
}
};
public static final StringBuilder getStringBuilder() {
return threadLocalStringBuilderHelper.get().getStringBuilder();
}
static final class StringBuilderHelper {
final StringBuilder sb;
StringBuilderHelper() {
sb = new StringBuilder();
}
StringBuilder getStringBuilder() {
sb.setLength(0);
return sb;
}
}
}