• 用大白话的方式讲明白Java的StringBuilder、StringBuffer的扩容机制


    StringBufferStringBuilder,都是继承了AbstractStringBuilder,它们的底层char数组value默认的初始化容量是16,扩容只需要修改底层的char数组,两者的扩容最终都会调用到AbstractStringBuilder类相同的方法:

    直入正题,扩容的步骤:

    1. 新的字符串的长度超过了底层原char数组value的大小,才需要进行扩容
    2. 先尝试默认扩容,将新容量变成 (value.length << 1) + 2 ,也就是两倍的原数组长度再加二
    3. 若默认扩充后的值还是小于至少容量的值,直接扩充到当前需要的至少容量大小;
    4. 经过前两步骤确定的新数组大小,若大于Interger.MAX_VALUE,则报异常,若小于等于0,则新数组大小改为Interger.MAX_VALUE -8
    5. 确定了新数组的值后,通过Arrays.copy(value,newCapactity)进行复制。最终给value数组完成扩容。

    这样扩容的目的,宏观上是尽可能地减少扩容次数,提高效率。

    肯定有同学会问,默认扩容为什么是两倍的原数组长度 + 2 ?

    因为源码并无说明这样设计的原因,所以根据我找到的资料结合我的推测,可能的原因有这些:

    • 考虑到在创建Sf,Sb设置的初始长度不大时(例如1),+2 可以很大地提升扩容的效率,减少扩容的次数
    • 在旧版本的JDK扩容语句是 (value.length + 1) * 2 先加一再乘2,推测原意思是扩容的话至少增添一个空间再乘2,兼顾到扩容的次数和要减少扩容过大浪费的空间
    • newCapacity(int)的传入参数有可能是0,那么在参数是0的情况下,0<<1运算结果也是0,如果没+2,那么在创建数组的时候会创建出MAX_ARRAY_SIZE大小,所以作为设计的安全性考虑,选择了+2。(本人认为除了反射调用newCapacity,其他情况应该不会出现newCapacity(int)可以传入0为参数)
    • append(str)后需补充分隔符所预留的位置,为了减少扩容次数 (个人感觉这点不太靠谱)

    下面是在JDK1.8中AbstractStringBuilder有关计算扩容的方法:

    //AbstractStringBuilder.java 
    
       private void ensureCapacityInternal(int minimumCapacity) {
        //若所需长度大于已有长度,才继续进行扩容
        if (minimumCapacity - value.length > 0) {
            //通过Arrays.copyOf(),将旧value数组内容先复制到newCapacity大小的数组,再赋值给新value
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        	}
    	}
        private int newCapacity(int minCapacity) {
            // 默认扩容:newCapacity = 两倍的原长度 + 2
            int newCapacity = (value.length << 1) + 2;
            if (newCapacity - minCapacity < 0) {//默认扩容后还是小于所需长度
                newCapacity = minCapacity;//直接补充至所需长度
            }
            return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
                ? hugeCapacity(minCapacity)//newCapacity>MAX_ARRAY_SIZE 或者≤0会调用
                : newCapacity;
        }
    
        MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        private int hugeCapacity(int minCapacity) {
            if (Integer.MAX_VALUE - minCapacity < 0) { //大小超出Integer范围爆异常
                throw new OutOfMemoryError();
            }
            return (minCapacity > MAX_ARRAY_SIZE) //返回minCapacity与MAX_ARRAY_SIZE最大值
                ? minCapacity : MAX_ARRAY_SIZE;
        }   
    
  • 相关阅读:
    Codeforces Round #551 (Div. 2) 题解
    【BZOJ5496】[十二省联考2019]字符串问题(后缀树)
    省选题记录
    【BZOJ5495】[十二省联考2019]异或粽子(主席树,贪心)
    Codeforces Global Round 2
    Android Studio安装使用图文教程(转)
    JAVA基础学习day15--集合二 TreeSet和泛型
    AndroidDevTools下载地址
    JAVA基础学习day14--集合一
    JAVA----编程列出一个字符串的全字符组合情况,原始字符串中没有重复字符
  • 原文地址:https://www.cnblogs.com/DMingO/p/13407303.html
Copyright © 2020-2023  润新知