JDK源码阅读之AbstractStringBuilder类

AbstractStringBuilder类的作用

AbstractStringBuilder是一个抽象类,也是StringBuilder和StringBuffer类的父类,这个类是这两个类的共同点的体现。

AbstractStringBuilder类的类图

AbstractStringBuilder类图
从类图中可以看出,

  1. 该类实现了Appendable接口,它的实现类的对象能够被添加 char 序列和值。如果某个类的实例打算接收取自 java.util.Formatter 的格式化输出,那么该类必须实现 Appendable 接口。
  2. 该类实现了CharSequence接口,CharSequence 是 char 值的一个可读序列。此接口对许多不同种类的 char 序列提供统一的只读访问

AbstractStringBuilder类的重点方法

属性变量

	/**
     * 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];
    }

构造方法也是非常简单,不过这里有个容易混淆的变量,capacity代表的容量,length代表的是长度,这两个变量是不同的。

精华方法

public void ensureCapacity(int minimumCapacity)

   // 确保容量大小
    public void ensureCapacity(int minimumCapacity) {
    	// 如果minimumCapacity 大于0,就开始内部确认容量
        if (minimumCapacity > 0)
            ensureCapacityInternal(minimumCapacity);
    }
	// 内部确认容量
	private void ensureCapacityInternal(int minimumCapacity) {
        // 如果minimumCapacity  > value.length ,那么确定扩容
        if (minimumCapacity - value.length > 0)
            expandCapacity(minimumCapacity);
    }
	// 扩容方法
	void expandCapacity(int minimumCapacity) {
		// 将容量扩大到原来的2倍+2
        int newCapacity = value.length * 2 + 2;
        //如果扩大后没有达到需要的容量,将需要的容量设定为容量值
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        // 将数组扩大,完成扩容
        value = Arrays.copyOf(value, newCapacity);
    }

从源码中分析可以得知:他采取的扩容策略是:length*2+2,如果没有达到要求,会将需要的容量设置为容量值。
个人认为:这个方法是这个类中的精华,也是比较多人会提起的。

常用方法

public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)

将字符从此序列复制到目标字符数组 dst。要复制的第一个字符在索引 srcBegin 处;要复制的最后一个字符在索引 srcEnd-1 处。要复制的字符总数为 srcEnd-srcBegin。要复制到 dst 子数组的字符从索引 dstBegin 处开始,

public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
    {
        if (srcBegin < 0)
            throw new StringIndexOutOfBoundsException(srcBegin);
        if ((srcEnd < 0) || (srcEnd > count))
            throw new StringIndexOutOfBoundsException(srcEnd);
        if (srcBegin > srcEnd)
            throw new StringIndexOutOfBoundsException("srcBegin > srcEnd");
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }

public AbstractStringBuilder append(String str)

按顺序追加 String 变量中的字符,使此序列增加该变量的长度。如果 str 为 null,则追加 4 个字符 “null”。

public AbstractStringBuilder append(String str) {
        if (str == null) str = "null";
        int len = str.length();
        //如果需要扩容,会在这个方法中进行扩容
        ensureCapacityInternal(count + len);
        // 将str中的值在value后添加
        str.getChars(0, len, value, count);
        // 改变长度
        count += len;
        return this;
    }

这个方法中需要注意的是扩容机制,因为如果不需要扩容的话,这个方法是很简单且高效的。

public AbstractStringBuilder delete(int start, int end)

移除此序列的子字符串中的字符。该子字符串从指定的 start 处开始,一直到索引 end - 1 处的字符,如果不存在这种字符,则一直到序列尾部。如果 start 等于 end,则不发生任何更改。

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 -= len;
        }
        return this;
    }
public AbstractStringBuilder deleteCharAt(int index) {
        if ((index < 0) || (index >= count))
            throw new StringIndexOutOfBoundsException(index);
        System.arraycopy(value, index+1, value, index, count-index-1);
        count--;
        return this;
    }

对于需要移除的序列,并没有做一个清空的操作,只是将后面的值覆盖前面的值,并将长度缩减而已。

public AbstractStringBuilder replace(int start, int end, String str)

使用给定 String 中的字符替换此序列的子字符串中的字符。该子字符串从指定的 start 处开始,一直到索引 end - 1 处的字符,如果不存在这种字符,则一直到序列尾部。先将子字符串中的字符移除,然后将指定的 String 插入 start。(如果需要,序列将延长以适应指定的字符串。)


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

这个方法的大概操作是这样的:先将value的值分为三段,第一段是删除区前面的称为d1,第二段是删除区称为d2,第三段是删除区之后的称为d3,

  1. 将d3的数据复制到 Vaule[start + len] 之后。(len的空间是为为替代的字符串空出来的)
  2. 将str的字符复制到value[start]之后

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

很简单,直接new出一个新对象返回

说明

这个类中的方法很多,我们有一个一个的复制出来,只是将我觉得有代表性的方法复制出来和大家分享。

AbstractStringBuilder类涉及的设计模式

暂无发现

AbstractStringBuilder类的阅读感想

这是一个抽象类,但是我看完源码之后发现,就只有toString()方法是抽象的,其他都不是。也都基本实现了所有的功能。我觉得这个类设计出来可能就是作为一个代表。就是提供一个模板给我们去实现自己的可变的字符串的类。
这个类的主要让我们学习的是他的扩容方法,这个方法我觉得思考的地方有两个:什么时候扩容?一次扩容要多少合适?从源码中“minimumCapacity - value.length > 0”我们可以看出,只有当需要的容量大于数组的容量的时候才会扩容。并且一次扩容的数量是上一次的两倍加2。
阅读这个源码,我发现这个类的源码很多,刚刚开始的时候有点恐惧,感觉好像要花很多的时间来阅读和理解,其实不然,当我们深入了解之后,越是往下读,越是感觉了然于胸。我读了一遍之后,往回去看,有一种油然而生的自豪感。

©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页