ArrayList Java内部实现
ArrayList在Java中的内部实现或者ArrayList在Java中如何内部工作是一个非常重要的面试问题。可能出现的一些问题是
- ArrayList在哪里存储其元素?
- ArrayList的初始或者默认容量是多少?如何创建该容量的ArrayList?
- ArrayList中的add方法如何工作?
- remove方法如何在ArrayList中工作?
- ArrayList如何自动增长和收缩?
在这篇文章中,我们通过研究Java中ArrayList的内部实现来尝试回答这些问题。
ArrayList在哪里存储其元素
Java内部的ArrayList使用数组存储其元素。这是一个Object数组,定义如下。
transient Object[] elementData;
ArrayList的容量
创建的ArrayList的初始容量取决于所使用的构造函数。有3个构造函数。
ArrayList(int initialCapacity)-如果在构造ArrayList时显式指定了初始容量,则可以确保使用该长度创建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); } }
从Java的ArrayList类的代码中可以看到,如果initialCapacity> 0,则使用该初始容量创建elementData数组。
ArrayList()–如果未指定初始容量,则使用默认容量创建ArrayList。
这是Java中ArrayList类的代码。
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
如果我们在代码中看到DEFAULTCAPACITY_EMPTY_ELEMENTDATA被定义为一个空数组。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
只有将元素添加到ArrayList时,才会使用默认容量创建阵列。 ArrayList类中的默认容量定义如下。
private static final int DEFAULT_CAPACITY = 10;
ArrayList(Collection <?extends E> c)–如果使用现有集合构造ArrayList,则将传递的集合的元素复制到elementData数组。
public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
从代码中可以看到,在此语句中elementData = c.toArray();集合的元素以数组形式返回。
elementData = Arrays.copyOf(elementData,size,Object []。class);
这一行确保将elementData数组转换为Object类型的数组。
add方法如何在ArrayList中工作
那就是覆盖ArrayList的可调整大小的数组实现功能的地方。如果我们看到Java中的ArrayList内部实现,则每次调用add()方法时,都会确保ArrayList具有所需的容量。如果容量用尽,则会创建一个新阵列,其容量比前一个阵列多50%。所有元素也将从先前的数组复制到新的数组。
最初调用add()方法时,将进行检查以确保容量。
private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); }
如我们所见,是否必须创建默认容量ArrayList,在这里实际将容量初始化为10.
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
根据需要,从此代码中调用grow()方法以增加ArrayList的容量。
/** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ 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); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
如我们所见,在以下语句中,使用右移运算符将容量增加了50%。
int newCapacity = oldCapacity + (oldCapacity >> 1);
还将elementData数组更改为具有新的容量,还将旧数组中的元素也复制到新数组中。
elementData = Arrays.copyOf(elementData, newCapacity);
这就是ArrayList在内部保持动态增长的方式。
删除方法如何在ArrayList中工作
如果从数组中删除任何元素,那么所有后续元素都将移动以填充由删除的元素创建的间隙。这就是remove方法在Java的ArrayList类中内部执行的操作。
/** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). */ 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; }
这是实际上在改组数组元素的语句。
System.arraycopy(elementData, index+1, elementData, index, numMoved);
这就是ArrayList动态缩小的方式。