一. 性能方面
StringBuilder > StringBuffer > concat > +
测试代码:
private static StringBuilder stringBuilder = new StringBuilder();
private static StringBuffer stringBuffer = new StringBuffer();
private static String string = "";
private static String string2 = "";
@Test
public void testAdd() {
long start = System.currentTimeMillis();
String str = "";
for (int i = 0; i < 400000; i++) {
str = str + "a";
}
System.out.println("1:"+ (System.currentTimeMillis() - start));
long start2 = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 400000; i++) {
sb.append("a");
}
System.out.println("2:"+(System.currentTimeMillis() - start2));
long start3 = System.currentTimeMillis();
StringBuffer sb2 = new StringBuffer();
for (int i = 0; i < 400000; i++) {
sb2.append("a");
}
System.out.println("3:"+(System.currentTimeMillis() - start3));
long start4 = System.currentTimeMillis();
String str4 = "";
for (int i = 0; i < 400000; i++) {
str4 = str4.concat("a");
}
System.out.println("4:"+(System.currentTimeMillis() - start4));
}
结果:
1:25787
2:6
3:6
4:17546
实现原理:
首先StringBuilder和StringBuffer的区别在于StringBuffer的所有public方法都加上了synchronized关键字,保证线程安全,而从根本实现层面来说,四者都是通过调用System.arraycopy()实现的:
1. StringBuilder:
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
可以看到,通常情况下,一次append()会调用两次System.arraycopy()方法
2. oncat:
public String concat(String var1) {
int var2 = var1.length();
if (var2 == 0) {
return this;
} else {
int var3 = this.value.length;
char[] var4 = Arrays.copyOf(this.value, var3 + var2);
var1.getChars(var4, var3);
return new String(var4, true);
}
}
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
void getChars(char[] var1, int var2) {
System.arraycopy(this.value, 0, var1, var2, this.value.length);
}
可以看到,一次concat()也是调用了两次System.arraycopy()方法,但是每次调用concat()方法都会调用一次new String()创建一个新的string对象,因此concat操作耗时较长
3. +
+运算符被重载,但是底层也是通过调用StringBuilder.append()实现的(jdk1.6 以上),而它之所以耗时是因为每次调用都会创建一个新的StringBuilder对象。
二 线程安全
测试代码和结果
@Test
public void threadSafeTest(){
ExecutorService executor = Executors.newCachedThreadPool();
for(int i = 0 ;i < 5000 ;i++){
executor.execute( () -> {
stringBuilder.append("a");
}
);
}
System.out.println("stringBuilder:"+stringBuilder.length());
long start2 = System.currentTimeMillis();
for(int i = 0 ;i < 5000 ;i++){
executor.execute( () -> {
stringBuffer.append("a");
}
);
}
System.out.println("stringBuffer:"+stringBuffer.length());
for(int i = 0 ;i < 5000 ;i++){
executor.execute( () -> {
string = string.concat("a");
}
);
}
System.out.println("concat:"+string.length());
for(int i = 0 ;i < 5000 ;i++){
executor.execute( () -> {
string2 = string2 + "a";
}
);
}
System.out.println("+:"+string2.length());
}
stringBuilder:4987
stringBuffer:5000
concat:4139
+:3260
可以看到,只有StringBuffer是线程安全的,其他几种拼接方式都不是线程安全的(如果length = 5000,那么代表是线程安全的)