从源码分析:分析Java中的StringBuilder

我们知道,在java中,如果要对于一个字符串进行操作,比如增删操作,如果直接用+来进行的话,效率是比较低的。作为替代,我们一般使用StringBuilder来进行这样的操作。

所以,这一次,我们就来研究一下StringBuilder的源码

构造函数

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

    /** use serialVersionUID for interoperability */
    static final long serialVersionUID = 4383685877147921099L;

    /**
     * Constructs a string builder with no characters in it and an
     * initial capacity of 16 characters.
     */
    public StringBuilder() {
        super(16);
    }

    /**
     * Constructs a string builder with no characters in it and an
     * initial capacity specified by the {@code capacity} argument.
     *
     * @param      capacity  the initial capacity.
     * @throws     NegativeArraySizeException  if the {@code capacity}
     *               argument is less than {@code 0}.
     */
    public StringBuilder(int capacity) {
        super(capacity);
    }

    /**
     * Constructs a string builder initialized to the contents of the
     * specified string. The initial capacity of the string builder is
     * {@code 16} plus the length of the string argument.
     *
     * @param   str   the initial contents of the buffer.
     */
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }

    /**
     * Constructs a string builder that contains the same characters
     * as the specified {@code CharSequence}. The initial capacity of
     * the string builder is {@code 16} plus the length of the
     * {@code CharSequence} argument.
     *
     * @param      seq   the sequence to copy.
     */
    public StringBuilder(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }

    ...

}

可以看到,当我们不指定初始的容量的时候,会调用父类AbstractStringBuilder的构造函数,并默认初始容量为16。

所以,我们来看看’AbstractStringBuilder’的构造方法:

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;

    /**
     * This no-arg constructor is necessary for serialization of subclasses.
     */
    AbstractStringBuilder() {
    }

    /**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

    ...

}

其中,主要关注:

AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

可以看到,在这里根据设定的初始容量,初始化出了一个相应长度的char型数组value

append方法

这里以str的append方法为例,看一下StringBuilder的append方法是怎样工作的。

首先是StringBuilder中的append(String str)方法。

public StringBuilder append(String str) {
    super.append(str);
    return this;
}

指向了父类中的append方法,那么就来看看父类中的定义:

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

其中的ensureCapacityInternal是为了保证容量能够装得下这个输入的str。

private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,
                newCapacity(minimumCapacity));
    }
}

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

private int hugeCapacity(int minCapacity) {
    if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
        throw new OutOfMemoryError();
    }
    return (minCapacity > MAX_ARRAY_SIZE)
        ? minCapacity : MAX_ARRAY_SIZE;
}

这里的扩容方式为先将容量乘2再加2,再与所需容量进行比较,若小于所需容量,则取所需容量,后面还有防止溢出的操作,写得非常严谨,这里值得我们学习与借鉴。获得更新后的容量后,新建一个新的容量的数组,并将之前的数据用Arrays.copyOf()方法复制进去,并更新为新的成员变量value

我们回到append方法中来,在进行完ensureCapacityInternal后,就要将所需添加的数据加进来了,这里调用的是StringgetChars方法。

# String.java
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);
}

这里调用System.arraycopy来将String内部的成员变量char型数组value(注意这里是String的,也就是append的参数里的那个变量)从0到str.length(),也就是所有成员,复制到dst中,也就是StringBuilder的成员变量value中。之后,便完成了整个append的操作。

posted @ 2019-07-12 17:10  点点爱梦  阅读(169)  评论(0编辑  收藏  举报