IT人生

  • 首页
  • 归档
  • kafka
  • Java
  • Spring
  • Golang
  • SQL
  • Spark
  • ElasticSearch
  • 关于

  • 搜索
Phoenix HBase Kudu ElasticSearch Spring 数据结构 操作系统 Kettle Azkaban Sqoop Hive Yarn Redis Mybatis Impala Cloudera 大数据 HDFS mycat shell Linux 架构 并发 mysql sql golang java 工具 spark kafka 人生

JDK源码分析-AbstractStringBuilder

发表于 2017-01-12 | 分类于 java | 0 | 阅读次数 191

系列文章:

  1. JDK源码分析-transient关键字
  2. JDK源码分析-transient关键字
  3. JDK源码分析-String
  4. JDK源码分析-AbstractStringBuilder
  5. JDK源码分析-StringBuffer和StringBuilder
  6. JDK源码分析-RandomAccess
  7. JDK源码分析-ArrayList
  8. JDK源码分析-LinkedList
  9. JDK源码分析-HashMap
  10. JDK源码分析-HashSet
  11. JDK源码分析-LinkedHashMap
  12. JDK源码分析-ConcurrentHashMap

类申明

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

成员变量

    /**
     * 内部是一个char[] value,不再是final的了,也就意味着可变
     * 这个char[] value 是核心
     */
    char[] value;

    /**
     * 记录已经使用的字符个数
     */
    int count;

核心方法

append方法

append方法提供了15个重载的方法

  1. append(Object obj)
  2. append(String str)
  3. append(StringBuffer sb)
  4. append(AbstractStringBuilder asb)
  5. append(CharSequence s)
  6. append(CharSequence s, int start, int end)
  7. append(char[] str)
  8. append(char str[], int offset, int len)
  9. append(boolean b) 以字符串'true'或者'false'内容插入
  10. append(char c)
  11. append(int i)
  12. append(long l)
  13. append(float f)
  14. append(double d)
  15. appendNull()比较特殊,下面会有讲到

其调用Sequence图如下 image.png

以append(String str)代码为例,

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

代码中第一步是检查参数str是否为空,当为空的时候,直接在原有的AbstractStringBuilder后面追加上null字符串

    private AbstractStringBuilder appendNull() {
        int c = count;
        //追加null字符串,并把原有长度扩大4
        ensureCapacityInternal(c + 4);
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;
        return this;
    }

char数组的长度在使用前是要初始化的,所有每次append新值的时候都会检查这个内部数组的长度,以防止数组越界

    //检查数据越界的核心代码
    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) { // 超过最大长度
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE)
            ? minCapacity : MAX_ARRAY_SIZE;
    }

当append(String str)其中str不为空的时候,把str字符串append到原有的AbstractStringBuilder的方法是调用了String的getChars方法

    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);
        }
        //native方法
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }

System.arraycopy是一个native方法,可以使用这个方法来实现数组之间的复制。对于一维数组来说,这种复制属性值传递,修改副本不会影响原来的值。对于二维或者一维数组中存放的是对象时,复制结果是一维的引用变量传递给副本的一维数组,修改副本时,会影响原来的数组。

package com.tutorialspoint;

import java.lang.*;

public class SystemDemo {

   public static void main(String[] args) {

      int arr1[] = { 0, 1, 2, 3, 4, 5 };
      int arr2[] = { 5, 10, 20, 30, 40, 50 };
    
      // 把arr1中的从0位置开的的数组,copy到arr2中起始位置为0长度为1的部分
      System.arraycopy(arr1, 0, arr2, 0, 1);
      System.out.print("array2 = ");
      System.out.print(arr2[0] + " ");
      System.out.print(arr2[1] + " ");
      System.out.print(arr2[2] + " ");
      System.out.print(arr2[3] + " ");
      System.out.print(arr2[4] + " ");
   }
}

//输出
array2 = 0 10 20 30 40 

具体用法参考:Java.lang.System.arraycopy() Method

insert方法

提供了12个重载的insert方法

  1. insert(int index, char[] str, int offset,int len)
  2. insert(int offset, Object obj)
  3. insert(int offset, String str)
  4. insert(int offset, char[] str)
  5. insert(int dstOffset, CharSequence s)
  6. insert(int dstOffset, CharSequence s,int start, int end)
  7. insert(int offset, boolean b)
  8. insert(int offset, char c)
  9. insert(int offset, int i)
  10. insert(int offset, long l)
  11. insert(int offset, float f)
  12. insert(int offset, double d)

image.png 其中最核心的方法是insert(int offset, String str),其他方法都是通过String.valueOf()来调用这个方法的。 而insert的逻辑和append逻辑大致相同,不同点在于多了指定位置的检查和插入。

    public AbstractStringBuilder insert(int offset, String str) {
        //检查插入的位置是否合法
        if ((offset < 0) || (offset > length()))
            throw new StringIndexOutOfBoundsException(offset);
        //null字符串的特殊处理
        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;
    }

reverse方法

image.png AbstractStringBuilder提供了一个reverse方法,可以翻转字符序列。 java采用UTF-16编码unicode,UTF-16使用使用一个6位单元或者两个16为单元表示一个unicode字符。使用两个单元的,前面那个单元叫highsurrogate 后面那个叫lowsurrogate

    public AbstractStringBuilder reverse() {
        //用于判断字符序列中是否包含surrogates pair,即表示unicode编码占用一个还是两个字符
        boolean hasSurrogates = false;
        int n = count - 1;
        //翻转算法是把整个char[]换分为前后,从中间到两头依次交换,(n-1) >> 1相当于(n-1)/2取得中轴
        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;
            //当一个字符是Surrogate时,特殊标记
            if (Character.isSurrogate(cj) ||
                Character.isSurrogate(ck)) {
                hasSurrogates = true;
            }
        }
        //如果序列中包含surrogates pair 则执行reverseAllValidSurrogatePairs
        if (hasSurrogates) {
            reverseAllValidSurrogatePairs();
        }
        return this;
    }

    //特殊处理Surrogate,即highsurrogate和lowsurrogate作为一对,把序列调整过来
    private void reverseAllValidSurrogatePairs() {
        for (int i = 0; i < count - 1; i++) {
            char c2 = value[i];
            if (Character.isLowSurrogate(c2)) {
                char c1 = value[i + 1];
                if (Character.isHighSurrogate(c1)) {
                    value[i++] = c1;
                    value[i] = c2;
                }
            }
        }
    }

补充一下:

StringBuffer stringBuffer = new StringBuffer("我在上海🌉");
System.out.println(stringBuffer.reverse());
System.out.println(stringBuffer.length());

输出:
🌉海上在我
6

从打印结果来看,整个字符串的长度是6位,而不是5位,其中🌉占了2位 🌉在java中的编码是"\uD83C\uDF09" , "\uD83C“就是lowsurrogate,而"\uDF09"是highsurrogate lowsurrogate和highsurrogate需要作为一个整体来翻转,而不能拆开

参考:

  1. https://zhuanlan.zhihu.com/p/53714077
  2. https://stackoverflow.com/questions/5903008/what-is-a-surrogate-pair-in-java/47505451#47505451
  • 本文作者: Randy
  • 本文链接: http://www.itrensheng.com/archives/AbstractStringBuilder
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# Phoenix # HBase # Kudu # ElasticSearch # Spring # 数据结构 # 操作系统 # Kettle # Azkaban # Sqoop # Hive # Yarn # Redis # Mybatis # Impala # Cloudera # 大数据 # HDFS # mycat # shell # Linux # 架构 # 并发 # mysql # sql # golang # java # 工具 # spark # kafka # 人生
JDK源码分析-String
JDK源码分析-StringBuffer和StringBuilder
  • 文章目录
  • 站点概览
Randy

Randy

技术可以暂时落后,但任何时候都要有上进的信念

80 日志
27 分类
31 标签
RSS
Github E-mail
Creative Commons
© 2021 备案号:沪ICP备19020689号-1
Randy的个人网站