(阿里巴巴开发手册)为什么阿里巴巴推荐内部员工使用StringBuilder?


今日我在阅读阿里巴巴开发手册泰山版,发现开发手册上有这么一条:【推荐】 循环体内,字符串的连接方式,使用 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