继承体系
ArrayList主要是继承自AbstractList抽象类并实现了List接口、实现了Cloneable和Serializable接口使得ArrayList具有克隆和序列化的功能、实现了RandomAccess接口以实现随机访问的功能。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
重要属性
private static final long serialVersionUID = 8683452581122892189L;
/**
* 默认的初始容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 一个共享的空的数组实例
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 一个共享的空的数组实例
* 与EMPTY_ELEMENTDATA区别在于当第一个元素被加入进来的时候它知道如何扩张
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 存放元素的数组空间
* 所以ArrayList其底层是基于数组实现的
* 用transient修饰,表示不可序列化
*/
transient Object[] elementData;
/**
* ArrayList中元素的数量
*/
private int size;
构造函数
/**
* 无参构造函数
* 构造一个初始容量为10的空列表
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 指定初始容量的构造函数
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list 初始容量
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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);
}
}
/**
* 使用传递的集合初始化List的构造函数
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection<? extends E> c) {
// 将集合c转换成数组元素,然后传递给elementData
elementData = c.toArray();
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 {
// replace with empty array.
// 使用默认的空的常量数组对象EMPTY_ELEMENTDATA
this.elementData = EMPTY_ELEMENTDATA;
}
}
add方法
1.add(E e)
- 进行空间检查,决定是否进行扩容,以及确定最少需要的容量
- 如果确定扩容,就执行grow(int minCapacity),minCapacity为最少需要的容量
- 第一次扩容,逻辑为newCapacity = oldCapacity + (oldCapacity >> 1);即在原有的容量基础上增加一半。
- 第一次扩容后,如果容量还是小于minCapacity,就将容量扩充为minCapacity。
- 对扩容后的容量进行判断,如果大于允许的最大容量MAX_ARRAY_SIZE,则将容量再次调整为MAX_ARRAY_SIZE。至此扩容操作结束。
/**
* 添加元素到list末尾
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
*/
public boolean add(E e) {
// 确保数组空间容量足够,容量>size+1
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
// 如果数组为默认的空数组实例DEFAULTCAPACITY_EMPTY_ELEMENTDATA
// 比较minCapacity与默认初始容量DEFAULT_CAPACITY,设为最大的数
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 确保数组空间大于minCapacity
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
// 定义父类AbstractList中的变量 protected transient int modCount = 0;
// 这个变量的主要作用是修饰结构化的修改此List的次数
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
// 扩容
grow(minCapacity);
}
/**
* ArrayList的最大容量
* 减8是因为某些VM会在数组中保留一些头字
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 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;
// 扩容,新的容量=当前容量+当前容量/2,即将当前容量增加一半,1.5倍扩容
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果扩容后的容量依旧小于想要的容量minCapacity,则将扩容容量改为minCapacity
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 如果想要的容量大于MAX_ARRAY_SIZE,则分配Integer.MAX_VALUE,否则分配MAX_ARRAY_SIZE
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
2.add(int index, E element)
- 越界检查
- 空间检查,如果有需要进行扩容
- 插入元素
/**
* 在指定位置插入元素
* 当前位置的元素和index之后的元素向后移一位
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
// 扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
// 对数组进行复制,空出index位置,将index之后的元素后移一位
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
// 容量加一
size++;
}
/**
* 检查下标是否越界
* A version of rangeCheck used by add and addAll.
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
3.addAll(Collection< ? extends E> c)
/**
* Appends all of the elements in the specified collection to the end of
* this list, in the order that they are returned by the
* specified collection's Iterator. The behavior of this operation is
* undefined if the specified collection is modified while the operation
* is in progress. (This implies that the behavior of this call is
* undefined if the specified collection is this list, and this
* list is nonempty.)
*
* @param c collection containing elements to be added to this list
*/
public boolean addAll(Collection<? extends E> c) {
// 获取添加的集合数组及长度
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
// 将数组a中的元素全部添加到原数组的后面
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
remove方法
- remove(int index)
/**
* 移除指定位置的元素
* 返回值为指定位置的元素
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
// 数组中index之后的元素进行左移一位
System.arraycopy(elementData, index+1, elementData, index, numMoved);
// 数量减一,并将索引为size-1处的元素置为null,为了进行GC操作,显式的为size-1处位置赋null值
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
/**
* 检查下标是否越界
* 下标小于0的判断是在elementData方法中判断的
* Checks if the given index is in range. If not, throws an appropriate
* runtime exception. This method does *not* check if the index is
* negative: It is always used immediately prior to an array access,
* which throws an ArrayIndexOutOfBoundsException if index is negative.
*/
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
2.remove(Object o)
/**
* 移除指定值的元素
* 返回值为true或false,是否存在指定值的元素
* Removes the first occurrence of the specified element from this list,
* if it is present. If the list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
*
* @param o element to be removed from this list, if present
*/
public boolean remove(Object o) {
// 空元素
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
// 使用循环遍历数组,判断该元素是否为指定删除的元素
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
// 删除操作
fastRemove(index);
return true;
}
}
return false;
}
/*
* 快速删除,跟remove(int index)方法类似,少了越界检查,及返回旧值
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(int index) {
modCount++;
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
}
3.removeAll(Collection< ?> c)
retainAll(Collection< ?> c)
/**
* 移除指定集合中的元素,只保留不在该集合中出现过的元素
* Removes from this list all of its elements that are contained in the
* specified collection.
*
* @param c collection containing elements to be removed from this list
*/
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
/**
* 移除不在指定集合中出现的元素,只保留在该集合中出现过的元素
* Retains only the elements in this list that are contained in the
* specified collection. In other words, removes from this list all
* of its elements that are not contained in the specified collection.
*
* @param c collection containing elements to be retained in this list
* @see Collection#contains(Object)
*/
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
/**
* complement 表示要保留的元素是否在集合c中出现,false表示不出现,true表示出现
*/
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
// 是否修改成功
boolean modified = false;
try {
// 两个索引进行判断,一快一慢进行移动
// r为快索引,存放检查到元素的下标,w为慢索引,存放实际保留元素的下标
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
// 如果符合保留条件,将该r下标的元素进行保存,放置到w下标上,w++
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
// 防止contains()抛出异常,r下标之后的元素没有判断
if (r != size) {
// 将r下标之后的元素移动到w下标之后,并修改w值
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
// 是否移除元素
if (w != size) {
// clear to let GC do its work
// 置w下标之后的元素为空,便于GC清除
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
// 更新size数量
size = w;
modified = true;
}
}
return modified;
}
get方法
/**
* 获取指定位置的元素
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
// 检查
rangeCheck(index);
// 直接从数组中取
return elementData(index);
}
set方法
/**
* 设定指定位置的元素,返回旧值
* Replaces the element at the specified position in this list with
* the specified element.
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
序列化
elementData使用transient修饰,即elementData无法被序列化。这样做是因为当我们序列化ArrayList的时候,此时ArrayList中的elementData并不一定是满的,即不一定每个空间都放置了实际数据,所以我们没必要序列化整个elementData。因此ArrayList实现了Serializable接口,重写了writeObject和readObject方法。
/**
* Save the state of the <tt>ArrayList</tt> instance to a stream (that
* is, serialize it).
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
// 序列化非transient元素
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
// 序列化数量,为读取反序列化做准备
s.writeInt(size);
// Write out all elements in the proper order.
// 遍历elementData数组,只序列化存在的实际数据元素
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// elementData设置为空的数组实例 EMPTY_ELEMENTDATA
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
// 读取数组的实际数据容量
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
// 确认容量符合数组空间大小,太小就扩容
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
// 读取每个元素,并赋值
a[i] = s.readObject();
}
}
}
iterator方法
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* The returned iterator is fail-fast
* 快速失效,即创建迭代器后,不能在外部手动进行修改原集合,否则该迭代器失效,需重新创建!
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
}
/**
* 内部类
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
// 迭代器的游标,指向下一个元素的下标,初始值为0
int cursor; // index of next element to return
// 指向当前元素的下标
int lastRet = -1; // index of last element returned; -1 if no such
// 修改次数,用于检测迭代过程中进行删除后,前后是否一致
int expectedModCount = modCount;
// 是否有下一元素
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
// 返回下一元素
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
// cursor指向下一位
cursor = i + 1;
// 更新lastRet,返回当前指向的元素
return (E) elementData[lastRet = i];
}
// 删除最近一次由next操作获取的元素,使用前必须调用next(),不能单独使用,否则抛出IllegalStateException异常
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
// 移除lastRet下标处的元素
ArrayList.this.remove(lastRet);
// 更新cursor为当前下标,并将lastRet至为-1
cursor = lastRet;
lastRet = -1;
// 更新修改次数
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
// 检查修改次数是否与预期相符合,否则抛出异常
// 快速失败机制
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
面试问题
- ArrayList插入删除一定慢么?
取决于你删除的元素离数组末端有多远,ArrayList拿来作为堆栈来用还是挺合适的,push和pop操作完全不涉及数据移动操作。 - ArrayList的遍历和LinkedList遍历性能比较如何?
论遍历ArrayList要比LinkedList快得多,ArrayList遍历最大的优势在于内存的连续性,CPU的内部缓存结构会缓存连续的内存片段,可以大幅降低读取内存的性能开销。 - ArrayList是如何扩容的?
ArrayList扩容后的大小等于扩容前大小的1.5倍,当ArrayList很大的时候,这样扩容还是挺浪费空间的,甚至会导致内存不足抛出OutOfMemoryError。扩容的时候还需要对数组进行拷贝,这个也挺费时的。所以我们使用的时候要竭力避免扩容,提供一个初始估计容量参数,以免扩容对性能带来较大影响。 - ArrayList是线程安全的么?
当然不是,线程安全版本的数组容器是Vector。Vector的实现很简单,就是把所有的方法统统加上synchronized就完事了。你也可以不使用Vector,用Collections.synchronizedList把一个普通ArrayList包装成一个线程安全版本的数组容器也可以,原理同Vector是一样的,就是给所有的方法套上一层synchronized。 - 数组用来做队列合适么?
队列一般是FIFO的,如果用ArrayList做队列,就需要在数组尾部追加数据,数组头部删除数组,反过来也可以。但是无论如何总会有一个操作会涉及到数组的数据搬迁,这个是比较耗费性能的。
所以ArrayList固然不适合做队列,但是数组是非常合适的。比如ArrayBlockingQueue内部实现就是一个环形队列,它是一个定长队列,内部是用一个定长数组来实现的。简单点说就是使用两个偏移量来标记数组的读位置和写位置,如果超过长度就折回到数组开头,前提是它们是定长数组。 - 多线程操作时,会出现什么问题?
数据丢失,添加null数据,下标越界,引用异常(非法修改)
由于 赋值操作elementData[size++] = e,扩容操作ensureCapacityInternal(size + 1),在多线程情况下数据不一致。