ArrayList就是数组列表,主要用来装载数据,当我们装载的是基本类型的数据int,long,boolean,short,byte…的时候我们只能存储他们对应的包装类,它的主要底层实现是数组Object[] elementData。
和LinkedList相比
LinkedList的底层是链表,所以拥有链表的特点,查询速度较数组慢,增删要快,而ArrayList的底层是数组实现的,增删慢,查询和访问元素的速度较快。
ArrayList的特点
增删慢,查询快,线程不安全,使用频率很高
ArrayList源码解析
1. ArrayList初始化分析
ArrayList初始化时,初始容量为0,只有真正对数据进行添加add时,才分配默认DEFAULT_CAPACITY = 10的初始容量。
分析
4个成员变量,下面的成员变量分别是默认容量、空对象数组、默认对象数组
从注释可以看出,elementData是一个对象数组缓存
1.1 有参构造函数
分析
可以看出,对于ArrayList,如果在new对象的时候传参数,也就是设置默认容量
如果默认容量大于0,elementData会指向一个容量为默认容量的对象数组
如果默认容量等于0,那么elementData会指向一个空对象数组的地址
如果默认容量小于0,因为容量不能为负,就会抛异常
1.2 无参构造函数
分析
可以看出,对于ArrayList,如果在new对象的时候不传参数,也就是不设置默认容量,那么elementData会指向一个默认空对象数组的地址
1.3 参数为集合的构造函数
2. ArrayList的add方法分析
数组的长度是有限制的,那么如果add元素超过了ArrayList容量会怎样呢?
下面是add方法的源码
ArrayList的增加元素时首先会调用方法 ensureCapacityInternal,去判断ArrayList的默认容量是否足够,如果不够就会扩容
我们先看看ensureCapacityInternal方法,参数minCapacity的值此时是传进来的size+1(size是int类型,初值为0)
继续点,看ensureExplicitCapacity方法
modCount用来记录修改次数,然后判断期望最小容量减去缓存数组的长度是否大于0
如果大于0,就调用grow方法,进行扩容
其实很好理解,因为第一次添加元素是,minCapacity的值是Size+1也就是1,第二次添加元素是2,也就是说minCapacity是存入元素的容量,所以也被称为期望最小容量,如果缓存数组的容量比期望的最小容量都要小,那么肯定放不下添加的元素,那么就必须调用grow方法进行扩容了。
3. ArrayList的扩容分析
数组的长度是有限制的,那么如果add元素超过了ArrayList容量会怎样呢?
在扩容的时候,老版本的jdk和8以后的版本是有区别的,8之后的效率更高了,采用了位运算,右移一位,其实就是除以2这个操作。
3.1 ArrayList的grow方法源码
从上文可知,minCapacity就是ArrayList添加元素之前的元素个数+1之后的期望最小容量(因为add每次只能添加一个元素,所以ArrayList的现在容量必须大于minCapacity,如果不大于就会扩容)
grow方法中,首先获取了扩容前的容量oldCapacity
新的容量为旧容量的1.5倍(旧容量加旧容量右移一位)
从这可以得出,ArrayList每次扩容都是原来的1.5倍,然后判断
如果新的容量减去期望最小容量小于0则新的容量就是最小期望容量
如果新的容量减去ArrayList的最大值也就是MAX_ARRAY_SIZE的话
看hugeCapacity方法
MAX_ARRAY_SIZE如下
Integer.MAX_VALUE如下