ArrayList 源码深度解析
一、重新认识ArrayList
- 什么是ArrayList?
ArrayList是基于数组实现的List类,封装了一个动态再分配的Object数组,以达到可以动态增长和缩减的索引序列。 - 长啥样?
如图,是一个长度为6,存储了6个6的数组,下标(index)从0开始,数组(elementData)表示存储的数据本身。 - 有哪些基本概念?
- index 表示数组下标;
- elementData 表示数组本身;
- DEFAULT_CAPACITY 表示数组初始大小,默认是10;
- size 表示当前数组的大小,int类型,未使用volatile修饰,非线程安全;
- modCount 表示当前数组被修改的版本的次数,也就是说当数组结构有变动,就会+1。
二、知其所以然----撸源码
1. 从整个ArrayList.java的类注释开始入手
简单翻译总结如下:
- ArrayList允许put null值,并且会自动扩容;
- size,isEmpty,get,set,Iterator和listIterator方法的时间复杂度是O(1),add方法的复杂度根据添加元素的多少而不同,添加n个元素,时间复杂度O(n);
- 非线程安全,因此在多线程情况下,为了线程安全推荐使用(List list = Collections.synchronizedList(new ArrayList(...));
);
- 支持增强for循环,但需要注意的是,由于非线程安全,所以在使用迭代器迭代过程中,如果数组大小被改变,会快速失败,并抛出异常。
2. 初始化
-
三种初始化方式,源码分析
- 无参数初始化
// 无参数构造器,默认是空数组 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
- 指定初始容量大小的初始化
// 构造一个具有指定初始容量的空列表 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); } }
- 指定初始数据初始化
//指定初始数据初始化 public ArrayList(Collection<? extends E> c) { //elementData 是保存数组的容器,默认为 null elementData = c.toArray(); //如果给定的集合(c)数据有值,则进行拷贝赋值操作 if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) //如果集合元素类型不是 Object 类型,才开始拷贝,否则不执行 if (elementData.getClass() != Object[].class) { elementData = Arrays.copyOf(elementData, size, Object[].class); } } else { // 给定集合(c)无值,则默认空数组 this.elementData = EMPTY_ELEMENTDATA; } }
- 无参数初始化
-
需要注意的地方
- 使用无参数构造器初始化时,默认容量时0,而不是传统印象里的10,只有接着对其进行add时才会变成10;
- 通过观察ArrayList的无参构造器,会发现当它进行初始化时,默认大小是空数组;
- 可以发现在指定初始数据初始化这种方式里,有一个see 6260652的注释,这是一个1.8版本的bug,在1.9版本的到了解决。解释是(toArray方法可能不会返回Object数组),也就是说当我们有一个ArrayList,里面的元素不是Object类型,比如是String,然后当我们调用toArray方法,得到一个Object数组后,再往这个String类型的Object数组赋值时,就会触发这个BUG,报错。官方查看文档地址:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652
3. 新增元素及扩容
- 先来看下新增元素核心源码
public boolean add(E e) { //确保数组大小足够,不够需要扩容 ensureCapacityInternal(size + 1); // Increments modCount!! //直接赋值,非线程安全 elementData[size++