注:本系列文章中用到的jdk版本均为
java8
ArrayList
类图以下:java
ArrayList
的底层是由数组实现的,数组的特色是固定
大小,而ArrayList
实现了动态扩容
。面试
ArrayList
部分变量以下,在下面的分析中会用到这些变量。数组
/** * 默认容量 */ private static final int DEFAULT_CAPACITY = 10; /** * 空的对象数组 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 无参构造器建立的空数组 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * 存放数据的数组的缓存变量 */ transient Object[] elementData; /** * 元素数量 */ private int size;
初始化ArrayList
通常会使用如下两个构造器缓存
初始化ArrayList
的时候若是不指定大小,则会建立一个空数组。安全
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); } }
ArrayList.add(E e)源码:dom
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
add()
中elementData[size++] = e
很好理解,就是将元素插入第size
个位置,而后将size++
,咱们重点来看看ensureCapacityInternal(size + 1)
方法;性能
private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); }
ensureCapacityInternal()
方法中判断缓存变量elementData
是否为空,也就是判断是不是第一次添加元素,若是是第一次添加元素,则设置初始化大小为默认容量10
,不然为传入的参数。这个方法的目的就是获取初始化数组容量。获取到初始化容量后调用ensureExplicitCapacity(minCapacity)
方法;this
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
ensureExplicitCapacity(minCapacity)
方法用来判断是否须要扩容,假如第一次添加元素,minCapacity
为10
,elementData
容量为0
,那么就须要去扩容。调用grow(minCapacity)
方法。spa
// 数组的最大容量 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; // 扩容大小为原来数组长度的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容容量比须要扩容的长度小,则使用须要扩容的容量 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 扩容容量比最大数组长度大,则使用最大整数长度 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
grow(minCapacity)
方法对数组进行扩容,扩容大小为原数组的1.5
倍,若是计算出的扩容容量比须要的容量小,则扩容大小为须要的容量,若是扩容容量比数组最大容量大,则调用hugeCapacity(minCapacity)
方法,将数组扩容为整数的最大长度,而后将elemetData
数组指向新扩容的内存空间并将元素复制到新空间。
当须要的集合容量特别大时,扩容1.5
倍就会很是消耗空间,所以建议初始化时预估一个容量大小。
ArrayList
提供两种删除元素的方法,能够经过索引
和元素
进行删除。两种删除大同小异,删除元素后,将后面的元素一次向前移动。
ArrayList.remove(int index)源码:
public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }
删除元素时,首先会判断索引是否大于ArrayList
的大小,若是索引范围正确,则将索引位置的下一个元素赋值到索引位置,将ArrayList
的大小-1
,最后返回移除的元素。操做图以下,假如我要移除索引为1
的元素:
ArrayList
底层是数组实现的,能够进行动态扩容,扩容大小为原来的1.5倍,虽然能够经过动态扩容,可是数组很是大时会特别浪费空间,所以建议初始化时预估数组大小。ArrayList
容许插入重复值和空值。ArrayList
实现了RandomAccess
接口,支持快速随机访问,就是能够经过索引快速查到某个元素,所以遍历时使用for循环的方式效率更高。ArrayList
是线程不安全的,能够经过Collections.synchronizedList
将其转变为线程安全的集合,不过通常不会使用,Vector
和CopyOnWriteArrayList
是线程安全的,Vector
性能通常,逐渐被CopyOnWriteArrayList
取代了。
若是以为文章不错,欢迎关注、点赞、收藏,大家的支持是我创做的动力,感谢你们。
若是文章写的有问题,请不要吝惜文笔,欢迎留言指出,我会及时核查修改。
若是你还想看到更多别的东西,能够微信搜索「Java旅途」进行关注。「Java旅途」目前已经整理各类中间件的使用教程及各种Java相关的面试题。