zoukankan      html  css  js  c++  java
  • 从源码分析:分析Java中的StringBuilder

    我们知道,在java中,如果要对于一个字符串进行操作,比如增删操作,如果直接用+来进行的话,效率是比较低的。作为替代,我们一般使用StringBuilder来进行这样的操作。

    所以,这一次,我们就来研究一下StringBuilder的源码

    构造函数

    public final class StringBuilder
        extends AbstractStringBuilder
        implements java.io.Serializable, CharSequence
    {
    
        /** use serialVersionUID for interoperability */
        static final long serialVersionUID = 4383685877147921099L;
    
        /**
         * Constructs a string builder with no characters in it and an
         * initial capacity of 16 characters.
         */
        public StringBuilder() {
            super(16);
        }
    
        /**
         * Constructs a string builder with no characters in it and an
         * initial capacity specified by the {@code capacity} argument.
         *
         * @param      capacity  the initial capacity.
         * @throws     NegativeArraySizeException  if the {@code capacity}
         *               argument is less than {@code 0}.
         */
        public StringBuilder(int capacity) {
            super(capacity);
        }
    
        /**
         * Constructs a string builder initialized to the contents of the
         * specified string. The initial capacity of the string builder is
         * {@code 16} plus the length of the string argument.
         *
         * @param   str   the initial contents of the buffer.
         */
        public StringBuilder(String str) {
            super(str.length() + 16);
            append(str);
        }
    
        /**
         * Constructs a string builder that contains the same characters
         * as the specified {@code CharSequence}. The initial capacity of
         * the string builder is {@code 16} plus the length of the
         * {@code CharSequence} argument.
         *
         * @param      seq   the sequence to copy.
         */
        public StringBuilder(CharSequence seq) {
            this(seq.length() + 16);
            append(seq);
        }
    
        ...
    
    }
    

    可以看到,当我们不指定初始的容量的时候,会调用父类AbstractStringBuilder的构造函数,并默认初始容量为16。

    所以,我们来看看’AbstractStringBuilder’的构造方法:

    abstract class AbstractStringBuilder implements Appendable, CharSequence {
        /**
         * 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];
        }
    
        ...
    
    }
    

    其中,主要关注:

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

    可以看到,在这里根据设定的初始容量,初始化出了一个相应长度的char型数组value

    append方法

    这里以str的append方法为例,看一下StringBuilder的append方法是怎样工作的。

    首先是StringBuilder中的append(String str)方法。

    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
    

    指向了父类中的append方法,那么就来看看父类中的定义:

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

    其中的ensureCapacityInternal是为了保证容量能够装得下这个输入的str。

    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) { // overflow
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE)
            ? minCapacity : MAX_ARRAY_SIZE;
    }
    

    这里的扩容方式为先将容量乘2再加2,再与所需容量进行比较,若小于所需容量,则取所需容量,后面还有防止溢出的操作,写得非常严谨,这里值得我们学习与借鉴。获得更新后的容量后,新建一个新的容量的数组,并将之前的数据用Arrays.copyOf()方法复制进去,并更新为新的成员变量value

    我们回到append方法中来,在进行完ensureCapacityInternal后,就要将所需添加的数据加进来了,这里调用的是StringgetChars方法。

    # String.java
    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);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }
    

    这里调用System.arraycopy来将String内部的成员变量char型数组value(注意这里是String的,也就是append的参数里的那个变量)从0到str.length(),也就是所有成员,复制到dst中,也就是StringBuilder的成员变量value中。之后,便完成了整个append的操作。

  • 相关阅读:
    Selenium系列(十五)
    Selenium系列(十四)
    Selenium系列(十三)
    Selenium系列(十二)
    Linux常用命令
    Linux
    Linux常用命令
    Linux常用命令
    Mysql常用sql语句(2)- 操作数据表
    Linux常用命令
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744385.html
Copyright © 2011-2022 走看看