StringBuilder源码分析

2019-05-25  本文已影响0人  EdwardWinner

StringBuilder类提供了众多拼接、插入字符的方法,因此在一定情况下,使用很广泛,以下是对StringBuidler的部分解析,有兴趣的朋友可以查看源代码
/android-26/java/lang/StringBuilder.

StringBuilder类也封装了一个字符数组,定义如下:

char[] value;

String不同,它不是final的,可以修改。另外,与String不同,字符数组中不一定所有位置都已经被使用,它有一个实例变量,表示数组中已经使用的字符个数,定义如下:

int count;

StringBuilder继承自AbstractStringBuilder,它的默认构造方法是:

public StringBuilder() {
    super(16);
}

调用父类的构造方法,父类对应的构造方法是:

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

也就是说,new StringBuilder()这句代码,内部会创建一个长度为16的字符数组,count的默认值为0。

append的实现

目前StringBuilder的append支持以下方法

@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}
public AbstractStringBuilder append(String str) {
    if (str == null) str = "null";
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }
}

append会直接拷贝字符到内部的字符数组中,如果字符数组长度不够,会进行扩展,实际使用的长度用count体现。具体来说,ensureCapacityInternal(count+len)会确保数组的长度足以容纳新添加的字符,str.getChars会拷贝新添加的字符到字符数组中,count+=len会增加实际使用的长度。

ensureCapacityInternal的代码如下:

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

newCapacity的代码如下:

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

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

如果MAX_ARRAY_SIZE - newCapacity < 0,则调用hugeCapacity方法进行增大容量,否则返回newCapacity的大小,hugeCapacity的代码是:

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

扩展的逻辑是,分配一个足够长度的新数组,然后将原内容拷贝到这个新数组中,最后让内部的字符数组指向这个新数组,这个逻辑主要靠下面这句代码实现:

value = Arrays.copyOf(value, newCapacity(minimumCapacity));

String类中的getChars方法

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
    if (dst == null) {
        throw new NullPointerException("dst == null");
    }

    if (srcBegin < 0) {
        throw new StringIndexOutOfBoundsException(this, srcBegin);
    }
    if (srcEnd > length()) {
        throw new StringIndexOutOfBoundsException(this, srcEnd);
    }

    int n = srcEnd - srcBegin;
    if (srcEnd < srcBegin) {
        throw new StringIndexOutOfBoundsException(this, srcBegin, n);
    }

    if (dstBegin < 0) {
        throw new ArrayIndexOutOfBoundsException("dstBegin < 0. dstBegin=" + dstBegin);
    }
    // dstBegin can be equal to dst.length, but only in the case where zero chars are to be
    // copied.
    if (dstBegin > dst.length) {
        throw new ArrayIndexOutOfBoundsException(
                "dstBegin > dst.length. dstBegin=" + dstBegin + ", dst.length=" + dst.length);
    }
    if (n > dst.length - dstBegin) {
        throw new ArrayIndexOutOfBoundsException(
                "n > dst.length - dstBegin. n=" + n + ", dst.length=" + dst.length
                        + "dstBegin=" + dstBegin);
    }

    getCharsNoCheck(srcBegin, srcEnd, dst, dstBegin);
}

insert实现

目前StringBuilder的insert支持以下方法

/**
 * @throws StringIndexOutOfBoundsException {@inheritDoc}
 */
@Override
public StringBuilder insert(int offset, String str) {
    super.insert(offset, str);
    return this;
}

父类中的实现

public AbstractStringBuilder insert(int offset, String str){
    if ((offset < 0) || (offset > length()))
        throw new StringIndexOutOfBoundsException(offset);
    if (str == null)
        str = "null";
    int len = str.length();
    ensureCapacityInternal(count + len);
    System.arraycopy(value, offset, value, offset + len, count - offset);
    str.getChars(value, offset);
    count += len;
    return this;
}

toString实现

字符串构建完后,我们来看toString代码:

@Override
public String toString() {
    if (count == 0) {
        return "";
    }
    return StringFactory.newStringFromChars(0, count, value);
}

StringFactory

目的是将长度从0到count的char[]转换成String

public static String newStringFromChars(char[] data) {
    return newStringFromChars(data, 0, data.length);
}

public static String newStringFromChars(char[] data, int offset, int charCount) {
    if ((offset | charCount) < 0 || charCount > data.length - offset) {
        throw new StringIndexOutOfBoundsException(data.length, offset, charCount);
    }
    return newStringFromChars(offset, charCount, data);
}

// The char array passed as {@code java_data} must not be a null reference.
@FastNative
static native String newStringFromChars(int offset, int charCount, char[] data);

TestStringBuilder

package com.example.test;

import java.util.ArrayList;

public class TestStringBuilder {

public static void main(String[] args) {
    testAppend();
    testInsert();
}

/**
 * 测试Append
 */
private static void testAppend() {
    StringBuilder appbuilder = new StringBuilder();
    // 追加字符数组
    appbuilder.append(new char[] { 'q', 'w', 'e', 'r', 't' });
    // 追加字符数组。0表示字符数组起始位置,4表示长度
    appbuilder.append(new char[] { 'A', 'S', 'D', 'F', 'G' }, 0, 4);
    // 追加float
    appbuilder.append(0.123f);
    // 追加double
    appbuilder.append(1.23456d);
    // 追加boolean
    appbuilder.append(false);
    // 追加char
    appbuilder.append('\n');
    // 追加int
    appbuilder.append(50);
    // 追加long
    appbuilder.append(11111L);
    // 追加StringBuilder对象
    appbuilder.append(new StringBuilder("TestStringBuilder"));
    // 追加StringBuilder对象。3表示被追加对象的起始位置(包括),12是结束位置(不包括)
    appbuilder.append(new StringBuilder("TestStringBuilder"), 3, 12);
    // 追加StringBuffer对象。
    appbuilder.append(new StringBuffer("TestStringBuffer"));
    // 追加StringBuffer对象。4表示被追加对象的起始位置(包括),14是结束位置(不包括)
    appbuilder.append(new StringBuffer("TestStringBuffer"), 4, 14);
    // 追加String对象。
    appbuilder.append("HelloWorld");
    // 追加String对象。1表示被追加对象的起始位置(包括),8是结束位置(不包括)
    appbuilder.append("WelcomeWorld", 1, 8);
    appbuilder.append('\n');

    // 在位置0处插入Object对象。此处以ArrayList为例
    ArrayList arrayList = new ArrayList();
    arrayList.add("1");
    arrayList.add("2");
    arrayList.add("3");
    appbuilder.append(arrayList);
    appbuilder.append('\n');

    System.out.printf("%s\n\n", appbuilder);
}

/**
 * 测试Insert
 */
private static void testInsert() {
    StringBuilder insertBuilder = new StringBuilder();
    // 在位置0处插入字符数组
    insertBuilder.insert(0, new char[] { 'q', 'w', 'e', 'r', 't' });
    // 在位置0处插入字符数组。0表示字符数组起始位置,4表示长度
    insertBuilder.insert(0, new char[] { 'A', 'S', 'D', 'F', 'G' }, 0, 4);
    // 在位置0处插入float
    insertBuilder.insert(0, 0.123f);
    // 在位置0处插入double
    insertBuilder.insert(0, 1.23456d);
    // 在位置0处插入boolean
    insertBuilder.insert(0, false);
    // 在位置0处插入char
    insertBuilder.insert(0, '\n');
    // 在位置0处插入int
    insertBuilder.insert(0, 50);
    // 在位置0处插入long
    insertBuilder.insert(0, 11111L);
    // 在位置0处插入StringBuilder对象
    insertBuilder.insert(0, new StringBuilder("TestStringBuilder"));
    // 在位置0处插入StringBuilder对象。3表示被在位置0处插入对象的起始位置(包括),12是结束位置(不包括)
    insertBuilder.insert(0, new StringBuilder("TestStringBuilder"), 3, 12);
    // 在位置0处插入StringBuffer对象。
    insertBuilder.insert(0, new StringBuffer("TestStringBuffer"));
    // 在位置0处插入StringBuffer对象。4表示被在位置0处插入对象的起始位置(包括),14是结束位置(不包括)
    insertBuilder.insert(0, new StringBuffer("TestStringBuffer"), 4, 14);
    // 在位置0处插入String对象。
    insertBuilder.insert(0, "HelloWorld");
    // 在位置0处插入String对象。1表示被在位置0处插入对象的起始位置(包括),8是结束位置(不包括)
    insertBuilder.insert(0, "WelcomeWorld", 1, 8);
    insertBuilder.insert(0, '\n');

    // 在位置0处插入Object对象。此处以ArrayList为例
    ArrayList arrayList = new ArrayList();
    arrayList.add("1");
    arrayList.add("2");
    arrayList.add("3");
    insertBuilder.insert(0, arrayList);
    System.out.printf("%s\n\n", insertBuilder);
}
}

有任何问题,欢迎指出.

上一篇 下一篇

猜你喜欢

热点阅读