美文网首页
Java下String和StringBuilder的append

Java下String和StringBuilder的append

作者: bingoc | 来源:发表于2016-03-08 22:21 被阅读464次

    源代码是万物之源。——黑客帝国

    先看下String的源代码:

    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence {
        /** The value is used for character storage. */
        private final char value[];//char数组也就是String的值
    
        /** Cache the hash code for the string */
        private int hash; // Default to 0
    
        /** use serialVersionUID from JDK 1.0.2 for interoperability */
        private static final long serialVersionUID = -6849794470754667710L;
    }
    

    所以其实String就是一个char[]。唯一需要注意的点应该就是final修饰符,也就意味着value是常量不可更改。这个和之前object-c中的设计很像,但不知道在这里是不是为了线程安全。从后面replace等函数的实现也可以看出,更改String的值需要重新申请一个String变量。

        public String replace(char oldChar, char newChar) {
            if (oldChar != newChar) {
                int len = value.length;
                int i = -1;
                char[] val = value; /* avoid getfield opcode */
    
                while (++i < len) {
                    if (val[i] == oldChar) {
                        break;
                    }
                }
                if (i < len) {
                    char buf[] = new char[len];
                    for (int j = 0; j < i; j++) {
                        buf[j] = val[j];
                    }
                    while (i < len) {
                        char c = val[i];
                        buf[i] = (c == oldChar) ? newChar : c;
                        i++;
                    }
                    return new String(buf, true);
                }
            }
            return this;
        }
    

    /* avoid getfield opcode */
    关于这个注释,查了一下。得到的答案是将变量复制到局部变量中,这样在下面的循环操作中就可以不用反复的从堆中取数据了。(栈中的访问速度更快,那是不是操作次数比较多的变量都可以用这种方法去增加速度呢?)

    但是在这个文件夹中我没有找到+号的重载函数(当然java本身不允许重载运算符),经过百度发现这个加号的重载是在编译阶段实现的。

    //以下两者是等价的
    s = i + ""
    s = String.valueOf(i);
    //以下两者也是等价的
    = "abc" + i;
    = new StringBuilder("abc").append(i).toString();

    再看下StringBuilder的源代码:

      /**
         * 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() {
        }
    

    可变的char[],以及长度。

      /**
         * Constructs a string builder with no characters in it and an
         * initial capacity of 16 characters.
         */
        public StringBuilder() {
            super(16);
        }
    

    这里对于为什么要在初始化的时候预留一个16个大小的数组还不太明白。

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

    这里就不贴所有的代码了,主要就是如果是对象则会调用string的valueof()转成String,如果是String就是调用getchars(),再对char[]添加赋值。
    所以其性能差异即在少做了一步string和StringBuilder的转化。
    而String s = "abc";这样的操作会在常量字符区生成一个"abc"常量,也会增加开销。

    java,StringBuilder预留16位

        public AbstractStringBuilder append(String str) {
            if (str == null)
                return appendNull();
            int len = str.length();
            ensureCapacityInternal(count + len);//在添加String时确定内部空间足够
            str.getChars(0, len, value, count);
            count += len;
            return this;
        }
        private void ensureCapacityInternal(int minimumCapacity) {
            // overflow-conscious code
            if (minimumCapacity - value.length > 0)//如果超过了预留的空间大小,则选择扩容。
                expandCapacity(minimumCapacity);
        }
        void expandCapacity(int minimumCapacity) {
            int newCapacity = value.length * 2 + 2;//将其扩充为2倍的大小加2;
            if (newCapacity - minimumCapacity < 0)
                newCapacity = minimumCapacity;//如果还不够大小,则将空间扩充为两个字符串大小之和
            if (newCapacity < 0) {
                if (minimumCapacity < 0) // overflow
                    throw new OutOfMemoryError();
                newCapacity = Integer.MAX_VALUE;//如果字符串之和小于int最大值,但是两倍太大会导致overflow,则将其设置为int最大值
            }
            value = Arrays.copyOf(value, newCapacity);
        }
    
    

    这样做最大的好处应该是在扩展小的字符串时不用每次都申请空间,只有在原有空间已满的时候再进行扩容,典型的空间换时间。

    相关文章

      网友评论

          本文标题:Java下String和StringBuilder的append

          本文链接:https://www.haomeiwen.com/subject/gtmzkttx.html