JDK源码阅读计划[Day3] AbstractStringBuilder,StringBuffer,StringBuilder

JDK11
代码参考的是 https://github.com/kangjianwei/LearningJDK/blob/master/src/java/lang/String.java

AbstractStringBuilder

这个类方法挺多的,1700多行,只挑一些重要而且陌生的记录下

/**
 * A mutable sequence of characters.
 * <p>
 * Implements a modifiable string. At any point in time it contains some
 * particular sequence of characters, but the length and content of the
 * sequence can be changed through certain method calls.
 *
 * <p>Unless otherwise noted, passing a {@code null} argument to a constructor
 * or method in this class will cause a {@link NullPointerException} to be
 * thrown.
 *
 * @author Michael McCloskey
 * @author Martin Buchholz
 * @author Ulf Zibis
 * @since 1.5
 */
// 字符序列的抽象实现,是StringBuilder和StringBuffer的父类
abstract class AbstractStringBuilder implements Appendable, CharSequence

1.从注释可以得知,AbstractStringBuilder的字符序列是可变的。
2.这是一个抽象类,子类是StringBuilder和StringBuffer

  • 成员
 /**
     * The maximum size of array to allocate (unless necessary).
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
    /**
     * The value is used for character storage.
     */
    // 以字节形式存储字符序列
    byte[] value;
    
    /**
     * The id of the encoding used to encode the bytes in {@code value}.
     */
    // 当前字符序列的编码:LATIN1或UTF16,由此可将ASB分为LATIN1-ASB或UTF16-ASB两类
    byte coder;
    
    /**
     * The count is the number of characters used.
     */
    // 当前ASB内包含的char的数量
    int count;
  • constructor
/**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    // 构造指定容量的ASB,内容为空
    AbstractStringBuilder(int capacity) {
        if(String.COMPACT_STRINGS) {
            value = new byte[capacity];
            coder = String.LATIN1;
        } else {
            value = StringUTF16.newBytesFor(capacity);
            coder = String.UTF16;
        }
    }
  • append方法

方法太多了,但是都是函数签名不同,大同小异。

/**
     * Documentation in subclasses because of synchro difference
     */
    // 向ASB末尾添加一个字符序列
    @Override
    public AbstractStringBuilder append(CharSequence s) {
        if(s == null) {
            return appendNull();
        }
        if(s instanceof String) {
            return this.append((String) s);
        }
        if(s instanceof AbstractStringBuilder) {
            return this.append((AbstractStringBuilder) s);
        }
        return this.append(s, 0, s.length());
    }

1.如果传进来的字符序列是空,调用以下函数

 // 添加一个字符串:"null"
    private AbstractStringBuilder appendNull() {
        ensureCapacityInternal(count + 4);
        int count = this.count;
        byte[] val = this.value;
        if(isLatin1()) {
            val[count++] = 'n';
            val[count++] = 'u';
            val[count++] = 'l';
            val[count++] = 'l';
        } else {
            // 将4个char依次存入UTF16-SB内部的字节
            count = StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l');
        }
        this.count = count;
        return this;
    }

2.根据字符序列的类型(String或者是AbstractStringBuilder)调用append并对s作强制类型转换

  • 逆序

LATIN1:交换对称位置的byte
UTF_16:byte转字节再交换

 public AbstractStringBuilder reverse() {
        byte[] val = this.value;
        int count = this.count;
        int coder = this.coder;
        int n = count - 1;
        if(String.COMPACT_STRINGS && coder == String.LATIN1) {
        //k和j为对称位置
            for(int j = (n - 1) >> 1; j >= 0; j--) {
                int k = n - j;
                byte cj = val[j];
                val[j] = val[k];
                val[k] = cj;
            }
        } else {
            StringUTF16.reverse(val, count);
        }
        return this;
    }
  • 比较
int compareTo(AbstractStringBuilder another) {
        if(this == another) {
            return 0;
        }
        
        byte val1[] = value;
        byte val2[] = another.value;
        int count1 = this.count;
        int count2 = another.count;
        
        if(coder == another.coder) {
            return isLatin1() ? StringLatin1.compareTo(val1, val2, count1, count2) : StringUTF16.compareTo(val1, val2, count1, count2);
        }
        
        return isLatin1() ? StringLatin1.compareToUTF16(val1, val2, count1, count2) : StringUTF16.compareToLatin1(val1, val2, count1, count2);
    }

1.先比较传进来的ASB与自身引用
2.本质上是比较字节数组的元素是否相同,在此之前先要对比两个ASB的编码方式
3.如果是Latin1则直接按字节比较,如果是UTF-16则先要把字节转换成字符

  • 删除操作
// 删除[start, end)范围内的char
    public AbstractStringBuilder delete(int start, int end) {
        int count = this.count;
        if(end>count) {
            end = count;
        }
        checkRangeSIOOBE(start, end, count);
        int len = end - start;  // 计算删除元素的数量
        if(len>0) {
            // 从end处的char开始,将后续所有的char平移-len个单位,负数则左移,正数右移
            shift(end, -len);
            this.count = count - len;
        }
        return this;
    }

1.如果end大于ASB的字节数量的话,那当然首先要把end设成当前最大的字节数量避免越界
2.然后核心思想是从end处的char开始,将后续所有的char平移-len个单位
调用的是arrayCopy这个本地方法:
数组复制,从src的srcPos索引处复制length个元素放入dest的destPos索引处

 private void shift(int offset, int n) {
        System.arraycopy(value, offset << coder, value, (offset + n) << coder, (count - offset) << coder);
    }
  • 删除索引为Index的字符串
 // 删除索引为index的char
    public AbstractStringBuilder deleteCharAt(int index) {
        String.checkIndex(index, count);
        // 从index+1处的char开始,将后续所有的char平移-1个单位,即删除一个cahr
        shift(index + 1, -1);
        count--;
        return this;
    }
  • 插入操作

方法也很多,这里只选一个

 // 向ASB的offset索引处插入一个字符串str
    public AbstractStringBuilder insert(int offset, String str) {
        String.checkOffset(offset, count);
        if(str == null) {
            str = "null";
        }
        int len = str.length();
        ensureCapacityInternal(count + len);
        shift(offset, len);
        count += len;
        // 向ASB的offset索引处插入一个字符串str
        putStringAt(offset, str);
        return this;
    }

一个严谨的函数,要对所有的参数都做异常判断处理:
这里的checkOffSet和字符串的判空,ensureCapacityInternal扩容函数都是好例子

private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        int oldCapacity = value.length >> coder;
        // 扩容
        if(minimumCapacity - oldCapacity>0) {
            // 本质是新创建一个容量为newCapacity
            value = Arrays.copyOf(value, newCapacity(minimumCapacity) << coder);
        }
    }

至于实现的核心思想就是用shift函数把字节数组中offset后的字节右移要插入的字符串长度位
然后调用putStringAt的getBytes函数

// 向ASB的index索引处插入一个字符串str
    private final void putStringAt(int index, String str) {
        if(getCoder() != str.coder()) {
            inflate();
        }
        str.getBytes(value, index, coder);
    }
// 拷贝String中的字节到dst数组
    void getBytes(byte dst[], int dstBegin, byte coder) {
        if(coder() == coder) {
            //                     src          dst
            System.arraycopy(value, 0, dst, dstBegin << coder, value.length);
        } else {
            /* 如果两个coder不同,则将源字符串当做LATIN-String对待 */
            // 从LATIN-String内部的字节转为UTF16-String内部的字节
            StringLatin1.inflate(value, 0, dst, dstBegin, value.length);
        }
    }
  • 求子串
   // 求ASB在[start, start+end)范围内的子串
    public String substring(int start, int end) {
        checkRangeSIOOBE(start, end, count);
        if(isLatin1()) {
            return StringLatin1.newString(value, start, end - start);
        }
        return StringUTF16.newString(value, start, end - start);
    }
// 用val在[index, index+len)范围内的byte值创建String
    public static String newString(byte[] val, int index, int len) {
        return new String(Arrays.copyOfRange(val, index, index + len), LATIN1);
    }

不难,本质上是字节数组拷贝出一个新的字符子串返回。

StringBuilder

public final class StringBuilder 
extends AbstractStringBuilder
implements Serializable, Comparable<StringBuilder>, CharSequence {

适用场景:单线程下操作大量字符,非线程安全
核心的append,insert,delete方法都是重写了父类ASB的方法,只是函数签名(参数类型不同)没啥好说的

建议比较下其与StringBuffer的实现

StringBuffer

public final class StringBuffer extends AbstractStringBuilder implements Serializable, Comparable<StringBuffer>, CharSequence {
    
    /**
     * A cache of the last value returned by toString. Cleared whenever the StringBuffer is modified.
     */
    // 调用toString()后生成的缓存,用于存储ASB中的字符序列。每次更改ASB都会清理缓存
    private transient String toStringCache;

第一个与StringBuilder不同的地方就在于多了个transient修饰的toStringCache成员,每次更改ASB之前都会先把这个cache设为null。

那么调用toString的时候就没必要再进行字节数组拷贝构建新字符串了!直接返回这个缓存就好

至于怎么实现线程安全,很简单,所有方法都用synchronized修饰,这也意味着其性能是远远比StringBuilder要低的。当然,如果频繁进行StringBuffer的append操作,JVM开启了逃逸分析功能的话,是可以触发锁消除的,使得性能大大提升。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值