美文网首页
ArrayList扩容(jdk11.0.3)

ArrayList扩容(jdk11.0.3)

作者: 飞奔吧牛牛 | 来源:发表于2020-04-22 15:26 被阅读0次

    我们最熟悉的ArrayList,在jdk11中较jdk8中大体逻辑没变,在一些细节上做了点优化,代码形式上也有点变化。
    这是这两天对扩容的逻辑分析,已经在代码上做了注释。

    大致逻辑:新来了一些数据,放不下了,需要扩容,扩容多大呢?一半吧,扩容一半还是不够呢?需要多大就扩容到多大吧。最后还要对边界做判断,不能超出最大容量。
         /*
            对扩容后的容量和所需的最小容量进行比较,具体要取哪个值。此外还要对边界进行判断,防止其越界。
         */
        private int newCapacity(int minCapacity) {
            int oldCapacity = this.elementData.length;
            //新容量 = 原容量 + 原容量/2,由于位移运算性能更好,故此处使用>>1,来代替取模运算
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            //扩容一半仍然不够用,或则刚好够用
            /*
                注意:这里用减法而不直接用newCapacity <= minCapacity作为条件,是因为程序中减法是使用二进制计算,如果newCapacity超出了Integer.MAX_VALUE,
                则newCapacity是个负数,负数肯定小于正数,所以不能用直接比大小的方法!!!但是如果用这个负数减去正数,还是有可能得到正数的。
                假设minCapacity比oldCapacity大 t,这里的判断其实是在判断 (oldCapacity >> 1) 和 t的大小,即扩容一半的量和至少需要扩容的量之间的大小。
                因为不管是二进制运算还是十进制运算,都可以消去oldCapacity。(oldCapacity >> 1)和t都是正数,二者的减法运算可以反映出二者的大小。
             */
            if (newCapacity - minCapacity <= 0) {
                if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                    //原来容量为0的话,新的数组长度取>=10的值。为的是避免前期的频繁扩容。
                    //这一点算是对java1.8的优化吧。
                    return Math.max(10, minCapacity);
                } else if (minCapacity < 0) {
                    //最小容量已经越界
                    throw new OutOfMemoryError();
                } else {
                    //扩容一半后仍然不够用或者刚好够用,并且原来容量不为0,则设置容量为所需的最小容量。
                    return minCapacity;
                }
            } else {
                //扩容一半,新的容量够用了,且新容量 <= Integer.MAX_VALUE - 8,就使用新容量,否则说明新容量。
                /*
                    这里为什么不直接和Integer.MAX_VALUE作比较呢?
                    这里面的2147483639,在java1.8中是:private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
                    其注释这样有这样的说明:
                        要分配的最大数组大小。
                        某些VM在数组中保留一些标头字。
                        尝试分配更大的数组可能会导致
                        OutOfMemoryError:请求的阵列大小超出了VM限制
                    嗯,标头字是什么意思?说是为了防止内存不足使用Integer.MAX_VALUE - 8作为最大容量,可最后hugeCapacity()中不还是使用了Integer.MAX_VALUE了吗?这个值的意义何在?
                 */
                return newCapacity - 2147483639 <= 0 ? newCapacity : hugeCapacity(minCapacity);
            }
        }
    
        private static int hugeCapacity(int minCapacity) {
            if (minCapacity < 0) {
                throw new OutOfMemoryError();
            } else {
                //为了防止一个很长的数组之后再做拷贝工作,如果所需的最小值位于(2147483639, 2147483647]之间,则直接取最大值2147483647。
                //newCapacity做了判断,这里再次用minCapacity和2147483639比较,可见,不到不得已,会尽量取<=2147483639的值作为新容量。
                return minCapacity > 2147483639 ? 2147483647 : 2147483639;
            }
        }
    

    还不明白的问题

    要分配的最大数组大小。
    某些VM在数组中保留一些标头字。
    尝试分配更大的数组可能会导致。OutOfMemoryError:请求的阵列大小超出了VM限制
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    

    有大佬可以给指个路吗?

    相关文章

      网友评论

          本文标题:ArrayList扩容(jdk11.0.3)

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