ArrayList 源码分析

开篇

ArrayList 是 Java 里面常用的一种集合,它的本质就是一个动态数组, 不仅满足了数组有序存储的特性,还弥补了数组无法动态扩容的缺陷。  	
ArrayList 的实现原理基于它内部维护了一个 Object 数组,当 ArrayList 容器中的数量达到一定限度时,会重新 new 一个更大的数组,并将原先数组中的数据拷贝过去。

源码分析(基于 jdk1.8.0_191 版本)

ArrayList 中比较常用的方法就是添加元素 add() 和 获取元素 get(),其中add() 方法比较复杂。所以分析的重心就放在了这个方法上,下面就开始一步一步解剖。

几个重要属性

  • DEFAULT_CAPACITY 默认容量
  • elementData ArrayList 内部维护的数组
  • EMPTY_ELEMENTDATA 一个空数组,被所有的空实例共享
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA 也是一个空数组, 与上面的区分开来。以便确定当添加了第一个成员之后扩充多大
  • size ArrayList 存放元素的个数

构造方法

ArrayList 里面一共有三个构造方法, 一个无参构造 和 两个有参构造,重点介绍两个关键的构造方法。
  1. 指定容量的有参构造

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
              initialCapacity);
        }
    }
    
  2. 无参构造

     public ArrayList() {
         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
     }
    

add() 方法

public boolean add(E e) {
    // 1⃣️ 确认容器中的容量, 扩容也就是在这一步进行的
    ensureCapacityInternal(size + 1);  // Increments modCount!!
     // 下面的代码就没什么好说的了,把元素放到指定索引上
    elementData[size++] = e;
    return true;
}

ensureCapacityInternal() 方法

private void ensureCapacityInternal(int minCapacity) {
     // 2⃣️ 下面分析 calculateCapacity
    ensureExplicitCapacity(calculateCapacity(elementData,   minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 如果通过 ArrayList 创建的实例, 那么在添加第一个元素时, 会得到一个容量为 10 的 ArrayList 实例
    // 第二次添加元素时,就不会走这一步了。
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // DEFAULT_CAPACITY = 10
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //  如果通过 ArrayList(initCapacity), 指定了初始容量,哪怕是 0 就直接返回。
    return minCapacity;
}

//  ③ 下面会拿一个空的容器来解释 也就是 通过new ArrayList()创建容器实例
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;// 这个变量可以先不管, 他表示 容器中 size 变化的次数。

    // 当添加第 1 个元素时, elementData.length的长度 为 0, minCapacity 为10,所以最终会创建一个容量为 10 的 ArrayList
    // 当添加第 2 个元素时, minCapacity为 1 ,elementData.length 为10, 无需扩容。
    // ......
    // 当添加第 11 个元素时,minCapacity为 11, elementData.length 为10, 会扩容1.5倍
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

 // ④ 具体的扩容算法
 private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 下面的暂不考虑
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 拷贝数组,并添加新元素
    elementData = Arrays.copyOf(elementData, newCapacity);
}

总结

1. 当通过 ArrayList() 构造一个空集合,初始长度是为0的,第 1 次添加元素,会创建一个长度为10的数组,并将该元素赋值到数组的第一个位置。
2. 第 2 次添加元素,集合不为空,而且由于集合的长度 size+1 是小于数组的长度 10,所以直接添加元素到数组的第二个位置,不用扩容。
3. 第 11 次添加元素,此时 size+1 = 11,而数组长度是10,这时候创建一个长度为10+10*0.5 = 15 的数组(扩容1.5倍), 
然后将原数组元素引用拷贝到新数组。并将第 11 次添加的元素赋值到新数组下标为10的位置。

转载于:https://my.oschina.net/u/3984985/blog/3018611

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值