今日我在阅读阿里巴巴开发手册泰山版,发现开发手册上有这么一条:【推荐】 循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。
字符串的三种表示类型
String类型
String是不可变类型。 下面是jdk11里面String类的源码,String的成员变量有value和hash。value是一个引用,指向String封装的数组对象。value是被private final修饰的,并且没有提供setvalue等用于修改值的公共方法。所以String是不可变的。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
}
每次对String的操作,一旦修改String对象,都会生成新的String对象。下面是String类中的replace()函数,每次修改String函数会调用,并return new String(buf, true);
。因此在循环体中,使用String进行连接不仅效率低下,而且造成内存资源浪费。
/**
* Returns a string resulting from replacing all occurrences of
* {@code oldChar} in this string with {@code newChar}.
*
* @param oldChar the old character.
* @param newChar the new character.
* @return a string derived from this string by replacing every
* occurrence of {@code oldChar} with {@code newChar}.
*/
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);//生成新的String对象
}
}
return this;
}
StringBuffer类型
StringBuffer类是线程安全的可变数据类型,继承于 AbstractStringBuilder类。
下面是jdk11里面StringBuffer类的源码。StringBuffer类具体实现了可变字符序列的操作比如append()、insert()、delete()、replace()、charAt()等。这些方法都被sychronized修饰,是线程安全的。
StringBuffer是线程安全的可变数据类型。StringBuffer类的对象能够被多次的修改,并且不产生新的未使用对象。对于不要求线程安全或者单线程的情况下,StringBuffer因为线程加锁导致性能损耗,所以在循环体中,使用StringBuffe效率低。
StringBuilder类型
StringBuilder是线程不安全的可变数据类型。与StringBuffer类一样,继承于 AbstractStringBuilder类。
下面是jdk11里面StringBuffer类的源码。StringBuffer类具体实现了可变字符序列的操作比如append()、insert()、delete()、replace()、charAt()等。与StringBuffer不同,这些方法没有加锁,不能降低性能,所以手册中建议使用StringBuilder。
String、StringBuilder和StringBuffer的性能测试
测试代码如下:
/**
* 测试String、StringBuilder和StringBuffer的性能
*
* @author : cxc
* @date : 2020-06-22 01:56
**/
public class Test {
public static Runtime runtime = Runtime.getRuntime();
public static void main(String[] args) {
long maxMemory = runtime.maxMemory();
System.out.println("最大内存: " + maxMemory/1024/1024 + "M");
int [] times = {10000, 100000, 1000000, 10000000, 100000000, 1000000000};
for (int i : times) {
System.out.println("循环执行次数:" + i);
StringTest(i);
StringBuilderTest(i);
StringBufferTest(i);
System.out.println();
}
}
private static void StringTest(int times){
String sbd = "";
long start = System.currentTimeMillis();
while (sbd.length() < times) {
sbd += "a";
}
System.out.println("String执行时间:" + (System.currentTimeMillis() - start)+"ms");
System.out.println("String使用内存: " + (runtime.totalMemory() - runtime.freeMemory())/1024/1024 + "M");
}
private static void StringBuilderTest(int times){
StringBuilder sbd = new StringBuilder();
long start = System.currentTimeMillis();
while (sbd.length() < times) {
sbd.append("a");
}
System.out.println("StringBuilder执行时间:" + (System.currentTimeMillis() - start)+"ms");
System.out.println("StringBuilder使用内存: " + (runtime.totalMemory() - runtime.freeMemory())/1024/1024 + "M");
}
private static void StringBufferTest(int times){
StringBuffer sbf = new StringBuffer();
long start = System.currentTimeMillis();
while (sbf.length() < times) {
sbf.append("a");
}
System.out.println("StringBuffer执行时间:" + (System.currentTimeMillis() - start)+"ms");
System.out.println("StringBuffer使用内存: " + (runtime.totalMemory() - runtime.freeMemory())/1024/1024 + "M");
}
}
执行结果如下:随着循环次数的增长,在循环体中StringBuilder的性能优势越来越明显。
参考链接:
https://blog.csdn.net/weixin_41101173/article/details/79677982