ArrayList源码阅读笔记(1.8)
ArrayList类的注解阅读
/**
* Resizable-array implementation of the <tt>List</tt> interface. Implements
* all optional list operations, and permits all elements, including
* <tt>null</tt>. In addition to implementing the <tt>List</tt> interface,
* this class provides methods to manipulate the size of the array that is
* used internally to store the list. (This class is roughly equivalent to
* <tt>Vector</tt>, except that it is unsynchronized.)
这是一个实现了List接口的可变长度的数组。实现了list接口中的所有方法,允许存放所有的元素,包括null。除了实现list接口,该类还提供了一些方法来操作数组的大小,此数组被用来存储list的数据。(这个类和vector大致相当,除了它是线程不安全的)
- ArrayList底层实现是数组,可存放重复数据,包括null,线程不安全
*
* <p>The <tt>size</tt>, <tt>isEmpty</tt>, <tt>get</tt>, <tt>set</tt>,
* <tt>iterator</tt>, and <tt>listIterator</tt> operations run in constant
* time. The <tt>add</tt> operation runs in <i>amortized constant time</i>,
* that is, adding n elements requires O(n) time. All of the other operations
* run in linear time (roughly speaking). The constant factor is low compared
* to that for the <tt>LinkedList</tt> implementation.
*
size(),isEmpty(),get(),set()方法使用的一个恒定时间(一个方法具有恒定的执行时间的,也就是代码不会因为问题规模n的变化而发生变化,时间复杂度记为O(1))。add操作花费恒定分摊时间,即插入n个元素需要o(n)的时间。 粗略的来说所有其他操作都以线性时间运行。(即这些操作与元素的个数成线性关系,操作的时间复杂度o(n))。这些操作与LinkedList实现相比,常数因子较低。
- 底层为数组结构,相对于LinkedList效率较高
* <p>Each <tt>ArrayList</tt> instance has a <i>capacity</i>. The capacity is
* the size of the array used to store the elements in the list. It is always
* at least as large as the list size. As elements are added to an ArrayList,
* its capacity grows automatically. The details of the growth policy are not
* specified beyond the fact that adding an element has constant amortized
* time cost.
*
每个ArrayList的实例对象都有一个容量(capacity)。这个容量就是这个list中用来存储元素的数组的大小。它至少和list的大小一样大。当有元素被增加到集合中时,它的容量会自动增加。除了要求添加一个元素的效率为“恒定分摊时间”,对于具体实现的细节没有特别的要求。
* <p>An application can increase the capacity of an <tt>ArrayList</tt> instance
* before adding a large number of elements using the <tt>ensureCapacity</tt>
* operation. This may reduce the amount of incremental reallocation.
在大批量插入元素前,使用ensureCapacity()方法来增加集合的容量。这或许能够减少扩容增加量的大小。
* <p><strong>Note that this implementation is not synchronized.</strong>
* If multiple threads access an <tt>ArrayList</tt> instance concurrently,
* and at least one of the threads modifies the list structurally, it
* <i>must</i> be synchronized externally. (A structural modification is
* any operation that adds or deletes one or more elements, or explicitly
* resizes the backing array; merely setting the value of an element is not
* a structural modification.) This is typically accomplished by
* synchronizing on some object that naturally encapsulates the list.
*
注意这个实现类是非同步的。如果有多个线程同时操作一个ArrayList的实例。然后,至少有一个线程修改了list的结构,就必须在外部保证它的线程同步。(结构修改指的是增加或者删除一个或多个映射;如果仅仅是更改已经存在的
key和value值,不算做结构修改)。这通常需要在一个被封装好的list对象上,使用同步进行操作。
- ArrayList是非同步的,线程不安全的
* If no such object exists, the list should be "wrapped" using the
* {@link Collections#synchronizedList Collections.synchronizedList}
* method. This is best done at creation time, to prevent accidental
* unsynchronized access to the list:<pre>
* List list = Collections.synchronizedList(new ArrayList(...));</pre>
*
如果没有这样的对象存在,那么就需要使用Collections.synchronizedList方法来包装这个list对象,而且最好是在创建对象的时候就进行包装,这是为了预防对这个list对象进行一些线程不同步的操作。举个例子:List list = Collections.synchronizedList(new ArrayList(...));
- 尽管有一个线程安全的类Vector和ArrayList结构类似,但是我们在需要保证线程安全时依然不会使用Vector这个类(过时的类),而是使用 Collections.synchronizedList()方法来包装得到一个线程安全的list对象。
* <p><a name="fail-fast">
* The iterators returned by this class's {@link #iterator() iterator} and
* {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:</a>
* if the list is structurally modified at any time after the iterator is
* created, in any way except through the iterator's own
* {@link ListIterator#remove() remove} or
* {@link ListIterator#add(Object) add} methods, the iterator will throw a
* {@link ConcurrentModificationException}. Thus, in the face of
* concurrent modification, the iterator fails quickly and cleanly, rather
* than risking arbitrary, non-deterministic behavior at an undetermined
* time in the future.
*
该类的集合视图方法返回的迭代器是fail-fast机制的:在迭代器被创建后,如果list对象被结构化修改后,无论在何时,使用何种方法(除了迭代器本身的remove方法和add方法)来修改它,都会抛出ConcurrentModificationException.因此,面对并发修改操作时,迭代器会迅速且清晰地报错.而不是冒着在不确定的时间做不确定的操作的风险.
- fail-fast,它是Java集合中的一种错误检测机制。某个线程在对collection进行迭代时,不允许其他线程对该collection进行结构上的修改。否则程序就会抛出ConcurrentModificationException 异常。快速终止操作
* <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
* as it is, generally speaking, impossible to make any hard guarantees in the
* presence of unsynchronized concurrent modification. Fail-fast iterators
* throw {@code ConcurrentModificationException} on a best-effort basis.
* Therefore, it would be wrong to write a program that depended on this
* exception for its correctness: <i>the fail-fast behavior of iterators
* should be used only to detect bugs.</i>
注意,迭代器的fail-fast行为是不能保证的.一般来说,保证非同步的同步操作是不太可能的.在最优基础上,Fail-fast迭代器会抛出ConcurrentModificationException.因此,写一个为了自身正确性而依赖于这个异常的程序是不对的.迭代器的fail-fast行为应该只是用来检测bug而已.
- 我们要主动封装list以便进行同步操作,程序要要避免此异常而不是使用此异常
ArrayList类的定义
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
-
继承的类
AbstractList:抽象类,只需知道此类是List接口的简单通用实现 -
实现的接口
List:不多说了
RandomAccess:(标记接口)代表支持随机访问
Cloneable:(标记接口)代表 Object.clone() 方法可以合法地对该类实例进行按字段复制。(没有实现 Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出 CloneNotSupportedException 异常)
java.io.Serializable(标记接口)
属性的定义
protected transient int modCount = 0;
- 这是父类AbstractList的一个属性 :用于记录列表结构被修改的次数。每次列表结构被修改都会modCount++
为什么要记录此数据呢?
在线程不安全的集合中,正如上面所说:迭代器采用了fail-fast机制。而fail-fast机制触发原理就是比对expectedModCount 和 modCount 是否相等,不相等就报ConcurrentModificationException异常
此处不理解没关系,后面会讲迭代器方法的源码时,就会明白了
/**
* Default initial capacity.
* 初始默认容量 为 10
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*
* 指定该ArrayList容量为0时,返回该空数组。
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*
* 用于默认大小的空实例的共享空数组实例。
* 这个空数组的实例用来给无参构造使用。当调用无参构造方法,返回的是该数组。
* 将此与EMPTY_ELEMENTDATA区分开来,以便了解在添加第一个元素时要增加多少容量。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
- EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA都是表示空数组实例
区别在于:
EMPTY_ELEMENTDATA 是用户指定初试容量为0时 使用它
DEFAULTCAPACITY_EMPTY_ELEMENTDATA 是默认创建初始容量为10的空数组,被无参构造器使用
将此与EMPTY_ELEMENTDATA区分开来,以便了解在添加第一个元素时要增加多少容量
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*
* 存储ArrayList元素的数组缓冲区
* ArrayList的容量(capacity)就是是此数组缓冲区的长度。
* 声明为transient 不会被序列化
* 非私有 是为了方便内部类调用
*/
transient Object[] elementData; // non-private to simplify nested class access
- 该值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时,当第一次添加元素进入ArrayList中时, 数组将扩容至DEFAULT_CAPACITY。这也印证了为什么区分EMPTY_ELEMENTDATA,DEFAULTCAPACITY_EMPTY_ELEMENTDATA
/**
* The size of the ArrayList (the number of elements it contains).
*
* List中元素的个数
*/
private int size;
- 仅仅表示 该list包含的元素的个数,和数组容量没有任何关系
这里提一点:前面提到size()方法时间复杂度为O(1),是因为它将size存储起来了,牺牲了空间提高了效率。
ArrayList构造器
ArrayList有三个构造器
创建一个指定存储容量的空序列
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);
}
}
- 指定的initialCapacity
>0时,创建的序列容量为initialCapacity
==0时,创建的是 EMPTY_ELEMENTDATA
<0时,会报异常
无参构造,创建一个初始容量为10 的空序列
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
- 此处使用的是DEFAULTCAPACITY_EMPTY_ELEMENTDATA
遵循集合的约定 提供 一个可将Collection转换为ArrayList的构造器
这些元素是按照该 Collection 的迭代器返回它们的顺序排列的。
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
- 注意:toArray的方法返回的类型不一定是Object[] 这是jDK的bug,两个类之间有继承关系时,JDK允许向上转型(子类赋值给父类)而 向下转型(父类赋值给子类)是不允许的。为了解决这个问题需要检验运行时类型使用Arrays.copyOf将其转为Object[]类型
核心方法
/**
* Trims the capacity of this <tt>ArrayList</tt> instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an <tt>ArrayList</tt> instance.
*
* 将ArrayList的容量设置为当前size的大小,应用可以使用此方法最小化实例的存储空间
*
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
- 注意:首先需要明确一个概念,ArrayList的size就是ArrayList的元素个数,length是ArrayList申请的内容空间长度。ArrayList每次都会预申请多一点空间,以便添加元素的时候不需要每次都进行扩容操作。此操作修改了列表结构,所以modCount++;
/**
* Increases the capacity of this <tt>ArrayList</tt> instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
* 如有必要,增加此ArrayList实例的容量,以确保它至少可以容纳由minCapacity参数指定的元
* 素数。
*
* @param minCapacity the desired minimum capacity
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
- ensureCapacity方法中,minExpand 表示最小扩容量, minCapacity表示最小容量,在这里分了两种情况
此实例 是默认大小的ArrayList,最小扩容量为DEFAULT_CAPACITY 否则为 0;然后比较minCapacity 和 minExpand,如果期望的最小容量比最小扩容量小就不扩容,反之使用minCapacity进行扩容
ensureCapacityInternal方法,数组容量检查,不够时则进行扩容,只供类内部使用,例如add(),addAll()等方法时会调用此方法进行容量检验,此时minCapacity 通常为size+n
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
// 只有最小容量比当前容量大时才会进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 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;
// 先预先扩容1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 主要判断 预期最小容量 minCapacity 是否大于 MAX_ARRAY_SIZE
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();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
总结:
- 只有最小容量比当前容量大时才会进行扩容;
- 扩容操作时,会预先把容量扩容至自身容量的1.5倍(预扩容)(使用的是oldCapacity + (oldCapacity >> 1)),然后于需要的最小容量minCapacity进行比较,选其最大值;
- 最终需要的容量会判断是否超过数组最大容量MAX_ARRAY_SIZE,1.没有超过 ——> 生成新的容量数组;2.超过了 ——> 会进行判断(1.是需要的最小容量超过了MAX_ARRAY_SIZE ——> 最终容量设为Integer.MAX_VALUE; 2.还是预扩容超过了MAX_ARRAY_SIZE ——> 最终容量设为MAX_ARRAY_SIZE) ——> 生成新的容量数组;
- 使用最大容量MAX_ARRAY_SIZE时,要注意因为某些VM会在数组中保留一些头字,可能会导致array容量大于VM的limit,最终导致OutOfMemoryError。
- minCapacity<0时也会触发MAX_ARRAY_SIZE,但这种情况在我的认知下不会发生,可能是严谨的做法,安全检查;
普通方法
public boolean contains(Object o) {
// 借用indexOf的方法看是否存在
return indexOf(o) >= 0;
}
// 实质就是遍历数组,使用Object的equals方法,返回-1为不存在此元素,lastIndexOf()方法就是反向遍历
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
public boolean add(E e) {
// 检验是否需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
// 先在index=size的位置赋值e,再将ArrayList的size增加一位
elementData[size++] = e;
return true;
}
/**
* 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!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
总结:
- add()一个参数直接加在数组最后
- add(index, element) 使用System.arraycopy()将index的位置空出来,将值赋值过去。System.arraycopy为 JVM 内部固有方法,它通过手工编写汇编或其他优化方法来进行 Java 数组拷贝,这种方式比起直接在 Java 上进行 for 循环或 clone 是更加高效的。数组越大体现地越明显。
/**
* 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)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 将数组最后一位 置 null,等待垃圾回收机制来回收
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
private class SubList extends AbstractList<E> implements RandomAccess {
迭代器(iterator&ListIterator)实现
ArrayList中的迭代器实现对AbstractList中的迭代器进行了优化
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* 按原有list的顺序返回一个迭代器,遵循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> {
// 后游标:指向下一个元素,等于下一个元素的索引值
int cursor; // index of next element to return
// 前游标:指向当前元素,等于当前元素索引值,-1代表在最开始,还未指向元素
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 = i + 1; // 后游标右移
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
// 调用ArrayList的remove
ArrayList.this.remove(lastRet);
// remove之后 后游标左移
cursor = lastRet;
// 重制前游标
lastRet = -1;
// 并且会使预期修改次数 和 实际修改次数 保持相等
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
// 检查 实际修改次数 和 预期修改次数是否相等
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
- 总结:
使用迭代器便利的时候,remove元素时,要使用Iterator.remove(),虽然其本质也是调用ArrayList的remove()的方法,只是为了使 modCount,expectedModCount 保持相等,否则会报ConcurrentModificationException()异常
/**
* Returns a list iterator over the elements in this list (in proper
* sequence), starting at the specified position in the list.
* The specified index indicates the first element that would be
* returned by an initial call to {@link ListIterator#next next}.
* An initial call to {@link ListIterator#previous previous} would
* return the element with the specified index minus one.
*
* <p>The returned list iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
/**
* Returns a list iterator over the elements in this list (in proper
* sequence).
*
* <p>The returned list iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @see #listIterator(int)
*/
public ListIterator<E> listIterator() {
return new ListItr(0);
}
/**
* An optimized version of AbstractList.ListItr
*/
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1; // 后游标右移
return (E) elementData[lastRet = i];
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
// 在当前元素的后面加入此元素
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
// add 操作会导致 modCount++,所以这边要保持相等
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
-
remove和add操作成功后 lastRet都会重新赋值为-1,并且会执行expectedModCount = modCount操作,以防ConcurrentModificationException;
-
ListItr继承于Itr,增加了previous()等一些方法,最大不同是游标cursor可以向前移动了。
-
注意:执行过next()方法后再执行previous(),当前元素并没有改变(lastRet没有变),只是cursor进行了了改变,不要进行常规思考 previous()操作就是当前元素向前移动,next()就是当前元素向后移动,准确来说是cursor进行前后移动,进行previous()操作时 lastRet始终等于cursor,next()操作时cursor始终比lastRet大1
最后声明
源码版本为JDK1.8,只是对日常使用的基本操作的源码进行了分析,对于1.8的新特性并没有涉及,等将主要集合类源码分析完后,会专门出一篇分析一下1.8中集合的新特性;
有建议或着问题的,请在文末留言,本人水平有限,有错误或理解偏差,还请各位多多指导和见谅,如若转载,请表明出处;