浅谈 StringBuilder

107 阅读4分钟

1.介绍

StringBuilder 是一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。底层是字符数组,与 String 不同的是该字符数组未被 final 修饰,所以可以修改。

public final class StringBuilder
    extends AbstractStringBuilder
    implements Serializable, CharSequence
{}
abstract class AbstractStringBuilder 
    implements Appendable, CharSequence {
    // 字符数组
    char[] value;

    // 当前字符串长度
    int count;
}

2.初始化

2.1 使用

方法描述
public StringBuilder()无参构造函数
public StringBuilder(int capacity)入参为容量的构造函数
public StringBuilder(String str)入参为字符串的构造函数
public StringBuilder(CharSequence seq)入参为 CharSequence 的构造函数
StringBuilder builder = new StringBuilder();
StringBuilder builder1 = new StringBuilder(12);
StringBuilder builder2 = new StringBuilder("ab");
StringBuilder builder3 = new StringBuilder(builder2);

2.2 源码简析

// 无参构造函数
// StringBuilder
public StringBuilder() {
    super(16);
}

// AbstractStringBuilder
AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}
// 入参为容量的构造函数
// StringBuilder
public StringBuilder(int capacity) {
    super(capacity);
}

// AbstractStringBuilder
AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}
// 入参为字符串的构造函数
// StringBuilder
public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
}
// 入参为 CharSequence 的构造函数
// StringBuilder
public StringBuilder(CharSequence seq) {
    this(seq.length() + 16);
    append(seq);
}


/**
 * CharSequence 是一个描述字符串结构的接口
 * 如 String、StringBuilder、StringBuffer 等都实现了该类
 */

3.尾部添加元素

3.1 使用

方法描述
public StringBuilder append(Object obj)添加 Object 元素
public StringBuilder append(String str)添加 String 元素
public StringBuilder append(StringBuffer sb)添加 StringBuffer 元素
public StringBuilder append(CharSequence s)添加 CharSequence 元素
public StringBuilder append(CharSequence s, int start, int end)添加指定范围的 CharSequence 元素
public StringBuilder append(char[] str)添加 char[] 元素
public StringBuilder append(char[] str, int offset, int len)添加指定范围的 char[] 元素
public StringBuilder append(boolean b)添加 boolean 元素
public StringBuilder append(char c)添加 char 元素
public StringBuilder append(int i)添加 int 元素
public StringBuilder append(long lng)添加 long 元素
public StringBuilder append(float f)添加 float 元素
public StringBuilder append(double d)添加 double 元素
StringBuilder builder = new StringBuilder();
builder.append("a").append(2).append('b');
// 结果为:a2b


StringBuilder builder2 = new StringBuilder();
builder2.append(new char[]{'a','b','c','d','e','f'},1,2);
// 结果为:bc

3.2 源码简述

3.2.1 入口函数

// AbstractStringBuilder$append
public AbstractStringBuilder append(String str) {
    // 字符串为空,则添加 null
    if (str == null)
        return appendNull();
    int len = str.length();
    // 判断是否需要扩容,需要则扩容
    ensureCapacityInternal(count + len);
    // 填充元素
    str.getChars(0, len, value, count);
    count += len;
    return this;
}
// offset:要添加的第一个字符的索引
// len:要添加到字符个数
public AbstractStringBuilder append(char str[], int offset, int len) {
    // 扩容操作
    if (len > 0)                
        ensureCapacityInternal(count + len);
    // 将 str 字符数组中指定范围内的字符复制到 value 字符数组中
    System.arraycopy(str, offset, value, count, len);
    count += len;
    return this;
}

3.2.2 扩容操作

// minimumCapacity:添加元素后所需的最小容量
private void ensureCapacityInternal(int minimumCapacity) {
    // 当所需的最小容量超过当前字符数组的长度时,则进行扩容
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,
                newCapacity(minimumCapacity));
    }
}

// 根据 minCapacity 计算新的容量
private int newCapacity(int minCapacity) {
    // 计算扩容后容量大小为 value.length*2 + 2
    int newCapacity = (value.length << 1) + 2;
    // 扩容后大小仍不够,则将容量扩大到 minCapacity
    if (newCapacity - minCapacity < 0) {
        newCapacity = minCapacity;
    }
    // 如果扩容后大小溢出 或 大于 Integer.MAX_VALUE - 8 = 2147483639,计算最大容量(hugeCapacity);否则返回新容量
    return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
        ? hugeCapacity(minCapacity)
        : newCapacity;
}

// 根据 minCapacity 计算最大容量
private int hugeCapacity(int minCapacity) {
    // 如果所需最小容量大于 Integer.MAX_VALUE,抛异常
    if (Integer.MAX_VALUE - minCapacity < 0) {
        throw new OutOfMemoryError();
    }
    return (minCapacity > MAX_ARRAY_SIZE)
        ? minCapacity : MAX_ARRAY_SIZE;
}

3.2.3 填充元素

// String$getChars
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);
}

4.指定位置插入元素

4.1 使用

方法描述
public StringBuilder insert(int index, char[] str, int offset, int len)在指定位置插入 str[offset,offet+len-1]
public StringBuilder insert(int offset, Object obj)在指定位置插入 Object 元素
public StringBuilder insert(int offset, String str)在指定位置插入 String 元素
public StringBuilder insert(int offset, char[] str)在指定位置插入 char[] 元素
public StringBuilder insert(int dstOffset, CharSequence s)在指定位置插入 CharSequence 元素
public StringBuilder insert(int dstOffset, CharSequence s, int start, int end)在指定位置插入 CharSequence[start,end) 元素
public StringBuilder insert(int offset, boolean b)在指定位置插入 boolean 元素
public StringBuilder insert(int offset, char c)在指定位置插入 char 元素
public StringBuilder insert(int offset, int i)在指定位置插入 int 元素
public StringBuilder insert(int offset, long l)在指定位置插入 long 元素
public StringBuilder insert(int offset, float f)在指定位置插入 float 元素
public StringBuilder insert(int offset, double d)在指定位置插入 double 元素
// 情况1
StringBuilder builder = new StringBuilder("abcdef");
builder.insert(1,"12");
// 结果为:a12bcdef

// 情况2
StringBuilder builder2 = new StringBuilder("abcdef");
builder2.insert(1,new char[]{'0','1','2','3','4','5','6'},1,2);
// 结果为:a12bcdef

// 情况3
StringBuilder builder3 = new StringBuilder("abcdef");
builder3.insert(1,"0123456",1,3);
// 结果为:a12bcdef

4.2 源码简述

4.2.1 情况1

StringBuilder.png

// 使用1
public AbstractStringBuilder insert(int offset, String str) {
    // offset 插入位置索引是否合法
    if ((offset < 0) || (offset > length()))
        throw new StringIndexOutOfBoundsException(offset);
    if (str == null)
        str = "null";
    int len = str.length();
    // 扩容操作
    ensureCapacityInternal(count + len);
    // 1. value[offset..count-1] 复制到 value[offset + len] 处
    System.arraycopy(value, offset, value, offset + len, count - offset);
    // 2. 填充元素
    str.getChars(value, offset);
    count += len;
    return this;
}

4.2.2 情况2

StringBuilder2.png

public AbstractStringBuilder insert(int index, char[] str, int offset, int len)
{
    // 判断插入位置是否合法
    if ((index < 0) || (index > length()))
        throw new StringIndexOutOfBoundsException(index);
    // 判断 str 字符数组的开始位置 offset 和 长度 len 是否合法
    if ((offset < 0) || (len < 0) || (offset > str.length - len))
        throw new StringIndexOutOfBoundsException(
            "offset " + offset + ", len " + len + ", str.length "
            + str.length);
    // 扩容操作
    ensureCapacityInternal(count + len);
    // 1. 将 value[index..count-1] 复制到 value[index+len] 处
    System.arraycopy(value, index, value, index + len, count - index);
    // 2. 将 str[offset,offset+len-1] 复制到 value[index,index+len-1] 处
    System.arraycopy(str, offset, value, index, len);
    count += len;
    return this;
}

4.2.3 情况3

StringBuilder3.png

public AbstractStringBuilder insert(int dstOffset, CharSequence s, int start, int end) {
    if (s == null)
        s = "null";
    // 判断插入位置是否合法
    if ((dstOffset < 0) || (dstOffset > this.length()))
        throw new IndexOutOfBoundsException("dstOffset "+dstOffset);
    // 判断 s 中的范围 [start,end) 是否合法
    if ((start < 0) || (end < 0) || (start > end) || (end > s.length()))
        throw new IndexOutOfBoundsException(
            "start " + start + ", end " + end + ", s.length() "
            + s.length());
    // 计算要插入的长度
    int len = end - start;
    ensureCapacityInternal(count + len);
    // 1. 将 value[dstOffset,count-1] 复制到 value[dstOffset+len] 处
    System.arraycopy(value, dstOffset, value, dstOffset + len, count - dstOffset);
    // 2. for 循环读取 s 的字符并复制到指定位置
    for (int i=start; i<end; i++)
        value[dstOffset++] = s.charAt(i);
    count += len;
    return this;
}

5.toString()

5.1 源码简述

StringBuilder 是字符数组,转换为字符串是使用 String 的构造函数传递数组进行转换。

// StringBuilder$toString
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}