美文网首页
ArrayList源码分析

ArrayList源码分析

作者: capo | 来源:发表于2017-08-28 21:41 被阅读33次

    简书:capo
    转载请注明原创出处,谢谢!

    今天,我们分析下JDK8中ArrayList的源码,我们知道ArrayList是List接口的一个实现,是一个动态扩容的数组。那么它是怎样在我们添加元素时实现动态扩容的了?

    这是数组定义的几个成员变量


    image.png

    首先我们调用数组的 add(E e) 方法时,这里我用的是一个重载的方法,第一个参数指的是要添加元素的下标,默认如果我们不提供这个参数的话是从数组的最后添加的。

    public void add(int var1, E var2) {
            //检查数组下标
            this.rangeCheckForAdd(var1);
            //保证数组内部容量 插入数组位置往后移动一个位置
            this.ensureCapacityInternal(this.size + 1);      
            //旧的数组就会使用Arrays.copyOf方法被复制到新的数组中,现有的数组指向了新的数组
            System.arraycopy(this.elementData, var1, this.elementData, var1 + 1, this.size - var1);
            this.elementData[var1] = var2;
            ++this.size;
        }
    

    Java会检查这个数组下标是否超过这个数组的长度或者下标小于0,如果是的话就抛出
    IndexOutOfBoundsException异常

     private void rangeCheckForAdd(int var1) {
            if(var1 > this.size || var1 < 0) {
                throw new IndexOutOfBoundsException(this.outOfBoundsMsg(var1));
            }
        }
    

    如果数组元素等于默认空数组的话,那么数组插入数组索引下标会进行一个取最大值的操作

    private void ensureCapacityInternal(int var1) {
           //如果添加的这个元素 == 默认的空数组的第一个元素
            if(this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                var1 = Math.max(10, var1); //在数组初始容量和数组的下标中取一个最大值
            }
            
            //明确保证数组的容量
            this.ensureExplicitCapacity(var1);
        }
    

    为了明确保证数组容量序列化计算

    private void ensureExplicitCapacity(int var1) {
             //反序列化计算器加 1 
            ++this.modCount;
            //如果要指定添加元素的下标位置 > 数组长度  此时就要扩容
            if(var1 - this.elementData.length > 0) {
                this.grow(var1);
            }
    
        }
    

    计算新数组的长度=原始数组的1.5倍

    private void grow(int var1) {
                 
            int var2 = this.elementData.length;
            //新的数组长度 = 原始数组长度 + 原始数组 / 2
            int var3 = var2 + (var2 >> 1);
            //如果新的数组长度 < 要添加元素的位置
            if(var3 - var1 < 0) {
                var3 = var1;//新数组长度 = 添加元素的位置长度
            }
            
            //如果新的数组长度 > 2147483639 
            if(var3 - 2147483639 > 0) {
                var3 = hugeCapacity(var1); //就判断是否超过了JVM内存
            }
    
            this.elementData = Arrays.copyOf(this.elementData, var3);
        }
    

    判断这个新数组长度是否JVM内存分配的最大值

    private static int hugeCapacity(int var0) {
            if(var0 < 0) {
                throw new OutOfMemoryError();
            } else {
                return var0 > 2147483639?2147483647:2147483639;
            }
        }
    

    总结一下:

    • ArrayList底层是一个动态扩容的数
    • 数组的初始长度是10个单位
    • 如果指定了要添加元素的索引下标的话,如果这个数组元素默认初始空数组的话就取一个最小值(添加元素的下标索引)
    • 扩容后新数组的长度 = 原始数组长度 + 原始数组长度/2 (也就是原始数组长度的1.5倍)
    • 数组的长度不是没有上限的,如果超过2147483639就会报OOM

    相关文章

      网友评论

          本文标题:ArrayList源码分析

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