JDK源码解读第三章:java.lang.AbstractStringBuilder

180 阅读5分钟

AbstractStringBuilder

1.AbstractStringBuilder声明

    abstract class AbstractStringBuilder implements Appendable, CharSequence 
  • 默认访问控制修饰符,说明只能在包内使用。
  • 类名用abstract修饰说明是一个抽象类,只能被继承,不能直接创建对象。
  • 实现了Appendable接口,Appendable能够被追加 char 序列和值的对象。如果某个类的实例打算接收来自 Formatter 的格式化输出,那么该类必须实现 Appendable 接口。
  • 实现了Charsequence接口,代表该类,或其子类是一个字符序列。

2.AbstractStringBuilder属性

    char[] value;
    int count;

value用于承装字符序列,count数组中实际存储字符的数量。value和string的不同,没有final修饰,所以可以进行动态操作。

3.AbstractStringBuilder构造函数

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

无参构造函数和支持传一个capacity(代表数组容量)的构造,这个构造函数用于指定类中value数组的初始大小,数组大小后面还可动态改变。

5.length()

    public int length() {
        return count;
    }

返回count,实际存储字符串长度。

6.capacity()

     public int capacity() {
        return value.length;
    }

返回value数组容量,和length()的区别在于,length()返回的实际字符长度,capacity()返回的是可存储字符长度。

6.ensureCapacity(int minimumCapacity)

    public void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > 0)
            ensureCapacityInternal(minimumCapacity);
    }
    
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
        	//扩容大小大于原数组大小,进行扩容,创建一个新容量的数组,把原数组数据拷贝到新数组中。
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }
    
    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) {
        	//取参数大小和原数组*2再加上2的大小中大的一个。所以如果参数小于原扩容大小,会以他自己的算法来进行扩容。
            newCapacity = minCapacity;
        }
         //判断如果扩容后大小在int范围内-8以内则扩容,否则执行hugeCapacity()
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }
    
    private int hugeCapacity(int minCapacity) {
        if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
        	//超出int范围,抛出OutOfMemoryError()异常。
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE)
            ? minCapacity : MAX_ARRAY_SIZE;
    }

内部数组扩容的核心方法。

7.trimToSize()

	public void trimToSize() {
        if (count < value.length) {
            //如果实际存储长度小于数组容联,拷贝一个新的只有实际长度的数组存储。
            value = Arrays.copyOf(value, count);
        }
    }

释放数组容量多余的存储空间,节约内存。

8.setLength(int var1)

    public void setLength(int newLength) {
        if (newLength < 0)
            throw new StringIndexOutOfBoundsException(newLength);
        ensureCapacityInternal(newLength);

        if (count < newLength) {
            Arrays.fill(value, count, newLength, '\0');
        }

        count = newLength;
    }

用空字符填充未使用的空间。首先对数组进行扩容,然后将剩余未使用的空间全部填充为'0'字符。

9.setLength(int var1)

    public char charAt(int index) {
        if ((index < 0) || (index >= count))
            throw new StringIndexOutOfBoundsException(index);
        return value[index];
    }

获取字符序列中指定位置的字符,范围为0到count,超出范围抛StringIndexOutOfBoundsException异常。

10.append(String var1)

	public AbstractStringBuilder append(String str) {
        if (str == null)
        	//如果参数为null,调用appendNull(),加上null字符串
            return appendNull();
        int len = str.length();
        //扩容
        ensureCapacityInternal(count + len);
        //把参数中的字符串添加到原字符串后面
        str.getChars(0, len, value, count);
        count += len;
        //返回原字符串,这里直接操作的源字符串,str.append("java").append("jdk");
        return this;
    }

    private AbstractStringBuilder appendNull() {
        int c = count;
        ensureCapacityInternal(c + 4);
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;
        return this;
    }

在原字符串结尾加上新的字符串。还有其他append方法,大同小异就不一一举例了。

11.delete(int var1, int var2)

    public AbstractStringBuilder delete(int start, int end) {
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (end > count)
            end = count;
        if (start > end)
            throw new StringIndexOutOfBoundsException();
        int len = end - start;
        if (len > 0) {
            System.arraycopy(value, start+len, value, start, count-end);
        	//如果目标数组和源数组为一个,进行的操作为,第一步先生成一个长度为count的临时数组,从源数组的start+len开始,复制长度为count-end的数组,第二步然后把临时数据相应的位置替换到源数组中得到结果为复制结果。第三步最后count等count减去删除长度得到最后的结果。下面是案例。
            System.arraycopy(value, start+len, value, start, count-end);
            count -= len;
        }
        return this;
    }

删除字符序列指定区间的内容。这个操作不改变原序列的容量。
案例:

public static void main(String[] args) {
        StringBuilder str = new StringBuilder("0123456") ;
        System.out.println(str.delete(2,4));
        //第一步结果:
        str = "0045600";
        //第二步结果:
        str = "0145656";
        //第三步结果:
        str = "01456";
    }

12.replace(int start, int end, String str)

    public AbstractStringBuilder replace(int start, int end, String str) {
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (start > count)
            throw new StringIndexOutOfBoundsException("start > length()");
        if (start > end)
            throw new StringIndexOutOfBoundsException("start > end");

        if (end > count)
            end = count;
        int len = str.length();
        //新数组容量
        int newCount = count + len - (end - start);
        //扩容
        ensureCapacityInternal(newCount);
        System.arraycopy(value, end, value, start + len, count - end);
        str.getChars(value, start);
        count = newCount;
        return this;
    }

将原字符序列指定区间start到end区间内的内容替换为str,替换过程中序列长度会改变,所以需要进行扩容和改就count的操作。

13.substring(int start, int end)

    public String substring(int start, int end) {
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (end > count)
            throw new StringIndexOutOfBoundsException(end);
        if (start > end)
            throw new StringIndexOutOfBoundsException(end - start);
        return new String(value, start, end - start);
    }

截取字符串,其实和string的截取是一样的。

14.insert(int offset, String str)

    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;
    }
    
    void getChars(char dst[], int dstBegin) {
        System.arraycopy(value, 0, dst, dstBegin, value.length);
    }
    
    StringBuilder str = new StringBuilder("0123456") ;
    System.out.println(str.insert(1,"qq"));
    //offset=1offset + len=3,count - offset=6
    System.arraycopy(value, offset, value, offset + len, count - offset);
    System.arraycopy(value, 1, value, 3, 6);
    第一步执行后,生成临时数据:
    value : 0121234 dst也是这个值
    下一步执行
    System.arraycopy("qq", 0, dst, 1, 2);
    结果:
    0qq1234
    count += len;
    结果:
    0qq123456

insert系列作用是将给定定对象所对应的字符串插入到原序列的指定位置。insert系列同append系列类似,只不过append是在原序列末尾添加元素,insert是在指定位置插入元素

15.reverse()

    public AbstractStringBuilder reverse() {
        boolean hasSurrogates = false;
        int n = count - 1;
        for (int j = (n-1) >> 1; j >= 0; j--) {
            int k = n - j;
            char cj = value[j];
            char ck = value[k];
            value[j] = ck;
            value[k] = cj;
            if (Character.isSurrogate(cj) ||
                Character.isSurrogate(ck)) {
                hasSurrogates = true;
            }
        }
        if (hasSurrogates) {
            reverseAllValidSurrogatePairs();
        }
        return this;
    }

该方法用于将字符序列反转,如"hellow"执行reverse后变成"wolleh"。
其实算法很简单,如果是大小为9,循环4次,依次调换,大小为10,循环5次,依次调换。 如果序列中包含surrogates pair 则执行reverseAllValidSurrogatePairs方法, Surrogate Pair是UTF-16中用于扩展字符而使用的编码方式,是一种采用四个字节(两个UTF-16编码)来表示一个字符。 char在java中是16位的,刚好是一个UTF-16编码。而字符串中可能含有Surrogate Pair,但他们是一个单一完整的字符,只不过是用两个char来表示而已,因此在反转字符串的过程中Surrogate Pairs 是不应该被反转的。而reverseAllValidSurrogatePairs方法就是对Surrogate Pair进行处理。

16.reverse()

    public abstract String toString();

这是这个抽象类中唯一的一个抽象方法,需要子类去实现。