前言

  本文针对于String类型相似的StringBuffer和 StringBuilder类的区别及StringBuffer和StringBuilder 类扩容。下面开始介绍。
先说一下String、StringBuffer、StringBuilder三者的异同如下:

StringStringBufferStringBuilder
不可变可变可变
线程安全线程不安全
多线程操作字符串单线程操作字符串

String、StringBuffer 和 StringBuilder 三者的继承结构如下:
image.png

String

  String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间。

特点
  • String类是被final修饰的,是不能被继承的。
  • String类底层使用数组结构。jdk9以前使用的是char[],jdk9以后使用的是byte[]。
  • String的对象一旦创建就不能修改,底层维护了一个字符串常量池,实现共享。

StringBuffer

  StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。

特点
  • 可变长
  • 线程安全的
  • 多线程操作
  • 效率低

StringBuilder

  StringBuilder是可变类,和线程不安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuilder对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。

特点
  • 可变长
  • 线程不安全的
  • 单线程操作
  • 效率高

StringBuffer和StringBuilder的初始容量及扩容

StringBuffer和StringBuilder初始的空闲容量都是16,当调用append方法时调用的都是super.append即AbstractStringBuilder中方法,所以扩容机制相同。

StringBuffer类有StringBuffer(),StringBuffer(int capacity),StringBuffer(String str)三个改造方法。

  • StringBuffer()的初始容量可以容纳16个字符,当该对象的实体存放的字符的长度大于16时,实体容量就自动增加。StringBuffer对象可以通过length()方法获取实体中存放的字符序列长度,通过capacity()方法来获取当前实体的实际容量。
    public StringBuilder() {
        super(16);
    }
  • StringBuffer(int capacity)可以指定分配给该对象的实体的初始容量参数为参数size指定的字符个数。当该对象的实体存放的字符序列的长度大于size个字符时,实体的容量就自动的增加。以便存放所增加的字符。
    public StringBuilder(int capacity) {
        super(capacity);
    }
  • StringBuffer(String str)可以指定给对象的实体的初始容量为参数字符串s的长度额外再加16个字符。当该对象的实体存放的字符序列长度大于size个字符时,实体的容量自动的增加,以便存放所增加的字符。
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }

接下来介绍StringBuffer怎样扩容:
StringBuffer通过append方法中的ensureCapacityInternal方法可以看出当追加后超出容量会触发扩容,通过newCapacity获得新容量。

    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));
        }
    }

计划扩容为2*n+2,n为扩容前容量,如果追加后长度超出则扩容为n+count,count为追加长度。由此可见下次append还会触发扩容机制。所以在设计时应该避免,最好能在初始时设置一个合理的容量。

    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2;
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }

总结

  1. 如果要操作少量的数据用 String;
  2. 多线程操作字符串缓冲区下操作大量数据 StringBuffer
  3. 单线程操作字符串缓冲区下操作大量数据 StringBuilder;
  4. StringBuffer和StringBuilder的区别就在于StringBuffer的操作使用synchronized关键字加了锁,是线程安全的。

Q.E.D.


知道的越多,不知道的越多