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的操作。

  • 相关阅读:
    LeetCode 252. Meeting Rooms
    LeetCode 161. One Edit Distance
    LeetCode 156. Binary Tree Upside Down
    LeetCode 173. Binary Search Tree Iterator
    LeetCode 285. Inorder Successor in BST
    LeetCode 305. Number of Islands II
    LeetCode 272. Closest Binary Search Tree Value II
    LeetCode 270. Closest Binary Search Tree Value
    LeetCode 329. Longest Increasing Path in a Matrix
    LintCode Subtree
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744385.html
Copyright © 2011-2022 走看看