ArrayList就是数组列表,主要用来装载数据,当我们装载的是基本类型的数据int,long,boolean,short,byte…的时候我们只能存储他们对应的包装类,它的主要底层实现是数组Object[] elementData。与它类似的是LinkedList,和LinkedList相比,它的查找和访问元素的速度较快,但新增,删除的速度较慢。
小结:ArrayList底层是用数组实现的存储。
特点:查询效率高,增删效率低,线程不安全。使用频率很高。
1. 初始化
ArrayList可以通过构造方法在初始化的时候指定底层数组的大小。通过无参构造方法的方式ArrayList()初始化,则赋值底层数 Object[] elementData 为一个默认空数组 Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {} 所以数组容量为0,只有真正对数据进行添加add时,才分配默认DEFAULT_CAPACITY = 10的初始容量。如果你传入了初始值大小,那就使用你传入的参数,如果没,那就使用默认的。下面为源码:
注意:ArrayList(int initialCapacity)并不会初始化数组的大小,这是属于 Java Bug中的一个经典问题。
2. ArrayList 的扩容机制
ArrayList 的底层数据结构为数组,数组的长度是有限制的,而ArrayList是可以存放任意数量对象,长度不受限制,它是通过数组扩容的方式去实现的。它的扩容机制为:它会重新定义一个新数组,新数组的长度为原来数组长度的 1.5 倍,然后把原数组复制到新的数组中,再把原数组的地址换到新数组上。
3. ArrayList 的 add 操作
在添加元素时,会先校验数组的长度,若长度不够则需要进行扩容。
在扩容的时候,老版本的jdk和8以后的版本是有区别的,8之后的效率更高了,采用了位运算,右移一位,其实就是除以2这个操作。
指定位置新增的时候,在校验之后的操作很简单,就是数组的copy,源码如下图:
4. ArrayList 的 remove 操作
删除其实跟新增是一样的,不过叫是叫删除,但是在代码里面我们发现,他还是在copy一个数组。
打个比方,我们现在要删除下面这个数组中的index5这个位置, 那代码他就复制一个index5+1开始到最后的数组,然后把它放到index开始的位置,index5的位置就成功被”删除“了其实就是被覆盖了,给了你被删除的感觉。同理他的效率也低,因为数组如果很大的话,一样需要复制和移动的位置就大了。
5. 线程安全问题
ArrayList 不是线程安全的,线程安全版本的数组容器是Vector。Vector的实现很简单,就是把所有的方法统统加上了synchronized 关键字。也可以不使用Vector,用 Collections.synchronizedList 把一个普通ArrayList包装成一个线程安全版本的数组容器也可以,原理同 Vector 是一样的,就是给所有的方法套上一层synchronized。