ArrayList简介
- ArrayList一种支持自动扩容的动态数组,继承了AbstractList实现了List,RandomAccess, Cloneable,java.io.Serializable,故支持随机访问,可克隆,可序列化等特性
- 粗略地讲,ArrayList和Vector功能基本等价,但是不同的是,ArrayList在多线程中式不安全,而Vector是线程安全的。如果存在多个线程访问ArrayList实例,且存在一个线程在修改该实例结构。可考虑使用同步的list或外部加锁或使用List list = Collections.synchronizedList(new ArrayList())
- fail-fast机制:iterator ListIterator 两种迭代在遍历列表时,发现此列表结构正在别改变(增加元素add addll,删除元素remove removeIf removeAll retainAll,清除clear都可以认为时结构改变)就会抛出一个ConcurrentModificationException异常,称这样机制为fail-fast机制
- 可存入null
- java8 引入了函数式编程,ArrayList对元素的处理更灵活
- ArrayList:说明ArrayList支持泛型
- ArrayList继承了AbstractList,AbstractList是List接口的骨干实现,以最大限度地减少“随机访问”数据存储(如ArrayList)实现Llist所需的工作
- implements List:实现了List接口,实现了所有可选列表操作
- implements RandomAccess:实现RandomAccess接口,说明ArrayList支持快速(通常是固定时间)随机访问,此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能
- implements Cloneable:实现Cloneable接口,表明其可以调用clone()方法来返回实例的field-for-field拷贝
- implements java.io.Serializable:实现Serializable接口,说明ArrayList具有序列化功能
注 检测结构改变的方法是:ArrayList类从AbstraList父类继承了一个modCount,每次执行add remove等方法改变结构时,modCount都会记录共修改了列表结构多少次,(如执行add(E e) 时,增加了一个元素,modCounth会加1),迭代器初始化时,会令一个成员变量int expectedModCount = modCount 每次迭代数组时(如执行next() previous()等方法),都会检测一下expectedModCount 是否 仍然等于 modCount(调用checkForComodification()),若发现不等说明,立即抛出ConcurrentModificationException,当然这种利用迭代器遍历时,尽最大努力的保障fail-fast机制。(尽最大努力 是由于并发的不确定性导致的,如刚刚检查完expectedModCount modCount的大小后,另外一个线程修改了列表结构,可能就检查不到)
ArrayList基础属性
1. 基础字段
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
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.
*/
private static final Object[] DEFAULTCAPACITY_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.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
/**
* 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;
- DEFAULT_CAPACITY 默认初始化容量* EMPTY_ELEMENTDATA 初始空数组,当利用ArrayList(int initialCapacity)构造函数初始化时,elementData = EMPTY_ELEMENTDATA* DEFAULTCAPACITY_EMPTY_ELEMENTDATA 默认初始容量下的空数组,这样我们知道在添加第一个元素的时候,应该扩容多少,当利用ArrayList<>()构造函数初始化时,elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA* elementData 存储ArrayList元素的数组,其长度(elementData.length())为ArrayList数组的容量* size list的容量大小,ArrayList中存放的实际元素个数* MAX_ARRAY_SIZE 有些虚拟机种支持ArrayList中允许最大的size,有些虚拟机也可以扩容为Integer.MAX_VALUE* protected transient int modCount = 0; 从AbstractList继承而来,主要用于fail-fast机制,检查列表结构在迭代过程中有没有被修改
2. 构造函数
/**
* 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);
}
}
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 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) {
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;
}
}
- ArrayList(int initialCapacity) ArrayList(int) 参数为初始化列表大小,默认情况为10, 当需要反复扩容时,建议先预估列表大小,提高性能* ArrayList() 构造一个初始化容量为10的列表* ArrayList(Collection<? extends E> c) 构造一个有具体元素的列表
ArrayList API概述
1. Override或新增的public方法
void trimToSize() //将列表的容量收缩为size,去掉capacity中没用元素用的位置
void ensureCapacity(int minCapacity) //确保列表的容量可容纳所有元素,否则扩容
int size() //返回列表大小
boolean isEmpty() //列表为空返回true,否则false
boolean contains(Object o) //判断列表是否包含对象o,
int indexOf(Object o) //顺序查找,返回对象o在列表中首次出现的位置, o不在列表中返回-1
int lastIndexOf(Object o) //逆序查找,返回对象o在首次出现的位置,不在返回-1
Object clone() //浅拷贝列表,如果列表中是基本类型,就是深拷贝,若是自定义类,就是浅拷贝
Object[] toArray() //列表转数组,是数组和列表之间的桥梁
<T> T[] toArray(T[] a) //列表转数组,返回数组的运行时类型是指定数组的运行时类型。
E elementData(int index) //按位置返回,对index的范围不做检查,返回第index位置上的元素
E get(int index) //按位置返回,检查index的范围,返回第index位置上的元素
E set(int index, E element) //将index位置的元素换为element,返回原来的element
boolean add(E e) // 增加element新元素
void add(int index,E element) //按位置插入,在index位置插入element,从index位置(包括index位置)开始整体后移1位
E remove(int index) //按位置移除,将index位置的元素移除,从index+1位置开始每个元素依次往前移动1位
boolean remove(Object o) //按元素移除,顺序找到待移除元素o,移除后,后面元素依次往前移动1位
void clean() //清空列表
boolean addAll(Collection<? extends E> c) //增加一个集合(多个元素),【先时后面的每个元素依次往后移动多个位置让出空间】
boolean addAll(int index, Collection<? extends E> c) //从index位置开始,增加一个集合(多个元素)【先时后面的每个元素依次往后移动多个位置让出空间】
boolean removeAll(Collection<? extends E> c) //如果列表中的元素存在在集合c中,那么将从列表中移除,即为保留列表与c的差集[列表-c]
boolean retainAll(Collection<? extends E> c) //如果列表中的元素存在在集合c中,那么将保留,即为取列表和c的交集
ListIterator<E> listIterator(int index) //双向迭代器【既可以前移,也可以往后移】,从第index位置开始迭代,可以利用该类型迭代器增插删元素【由于实现get,set,remove】
ListIterator<E> listIterator() //双向迭代器【既可以前移,也可以往后移】,从0位置开始迭代,
Iterator<E> iterator() //单向(顺序从前往后),可以利用该类型迭代器移除元素【由于实现remove】
List<E> subList(int fromIndex, int toIndex) //返回列表的一个子列表
void forEach(Consumer<? super E> action) //遍历每个元素,对每个元素做action操作
Spliterator<E> spliterator()
boolean removeIf(Predicate<? super E> filter) //移除列表中符合filter动作的元素
void replaceAll(UnaryOperator<E> operator)
void sort(Comparator<? super E> c) //根据c的规则排序
2. 超类或接口直接继承的default和常规方法
public int hashCode() //从AbstractList类直接继承,且ArrayList类中没有Override
public boolean equals(Object O) //从AbstractList类直接继承,且ArrayList类中没有Override
default Stream<E> stream() //串行流
default Stream<E> parallelStream() //并行流
3. ArrayList类内部private方法
void ensureCapacityInternal(int minCapacity) //检查扩容
static int calculateCapacity(Object[] elementData ,int) //检查是否需要扩容
void ensureExplicitCapacty(int),grow(int ) //检查是否需要扩容
void grow(int minCapacity) //常规扩容+ 按需扩容
static int hugeCapacity(int minCapacity) //当容量快达最大容量时的扩容操作
void fastRemove(int index) //按位置快速remove,快速体现在不对index做范围检查
void rangeCheck(int index) //对index做边界检查,防止越上界
void rangeCheckForAdd(int index) //对index做边界检查,防止越上下界
String outOfBoundsMsg(int index) //越界异常的提醒信息
boolean batchRemove(Collection<?> c, boolean complement) //批量remove,主要为removeAll retainAll两个public方法服务
void writeObject(java.io.ObjectOutputStream s) //序列化
void readObject(java.io.ObjectInputStream s) //序列化
ArrayList源码详解
1. add()方法
//添加元素到list末尾
public boolean add(E e) {
//确保list的容量大小,如果当前空间不足,容量加1
ensureCapacityInternal(size + 1); // Increments modCount!!
//将新元素添加在末尾位置
elementData[size++] = e;
return true;
}
//在指定位置插入元素。当前位置的元素和index之后的元素向后移一位
public void add(int index, E element) {
rangeCheckForAdd(index);
//确保list的容量大小,如果当前空间不足,容量加1。
ensureCapacityInternal(size + 1);
//将index位置之后的元素后移
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将元素添加在index位置
elementData[index] = element;
//容量加1
size++;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 数组容量检查,不够时则进行扩容
private void ensureCapacityInternal(int minCapacity) {
// 如果是空数组,取默认容量与所需容量的最大值为实际所需容量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
// 数组容量检查,不够时则进行扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 如果实际所需容量大于数组容量,扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//扩容,保证ArrayList至少能存储minCapacity个元素
private void grow(int minCapacity) {
// 获取当前数组的容量
int oldCapacity = elementData.length;
// 容量扩容为原来的1.5倍,新的容量=当前容量+当前容量/2,即将当前容量增加一半
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果扩容后的容量还是小于实际所需容量,则将扩容后的容量设置为实际所需容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果扩容后的容量超过了系统预设的最大值:Integer.MAX_VALUE - 8,检测是否溢出
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 使用Arrays的copyof方法将原数组数据拷贝到新的数组,并将新数组赋值给变量elementData
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 进行大容量分配
private static int hugeCapacity(int minCapacity) {
// 如果溢出,提示异常,如果没有溢出,实际所需容量是否超过系统预设的最大值,如果超过,返回Integer的最大值,如果没有超过,返回系统预设的最大值
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;
}
add方法实现大致流程:
- 判断数组容量是否为空,如果是,比较默认容量与实际容量大小,取最大值
- 判断实际所需容量如果大于数组容量,扩容
- 保存数据,size加1
2. get()方法
//返回list中索引为index的元素
public E get(int index) {
// 检查是否越界
rangeCheck(index);
//根据索引返回元素
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
3. remove()方法
//删除list中位置为指定索引index的元素
public E remove(int index) {
//检查索引是否越界。如果参数指定索引index>=size,抛出一个越界异常
rangeCheck(index);
//结构性修改次数+1
modCount++;
//记录索引为inde处的元素
E oldValue = elementData(index);
// 删除指定元素后,需要左移的元素个数
int numMoved = size - index - 1;
//如果有需要左移的元素,就移动(移动后,该删除的元素就已经被覆盖了)
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// size减一,然后将索引为size-1处的元素置为null。为了让GC起作用,必须显式的为最后一个位置赋null值
elementData[--size] = null; // clear to let GC do its work
//返回被删除的元素
return oldValue;
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
4. removeAll()方法
public boolean removeAll(Collection<?> c) {
// 参数非空校验
Objects.requireNonNull(c);
return batchRemove(c, false);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// 如果c.contains提示异常
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
// 如果有数据被删除
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
removeAll方法的实现在于,先遍历elementData,将elementData与另一个集合c没有交集的数据,放置在elementData的下标的0到w段,然后再清除掉下标w到size-1之间的元素就行了(即设置为null)
5. set()方法
//替换指定索引的元素
public E set(int index, E element) {
//检查索引是否越界。如果参数指定索引index>=size,抛出一个越界异常
rangeCheck(index);
//记录被替换的元素
E oldValue = elementData(index);
//替换元素
elementData[index] = element;
//返回被替换的元素
return oldValue;
}
6. trimToSize()方法
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
- 我们对数组扩容之后,有时候数组的容量会大于实际所需要的容量,这时候如果我们想将数组容量调整为实际所需要的容量,可以调用该方法* 比如内存紧张,或者我们可以确定不会再有元素添加进来时,也可以调用该方法来节省空间
7. ensureCapacity(int minCapacity)方法
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);
}
}
- 从add()与addAll()方法中可以看出,每当向数组中添加元素时,都要去检查添加元素后的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求* 在JDK8中,JDK提供了一个public的ensureCapacity方法让我们可以手动设置ArrayList的容量,以减少上面这种递增时调用再重新分配的数量
8. 其他
由于ArrayList实现了RandomAccess, Cloneable, java.io.Serializable,所以支持随机读取,复制,序列化操作,对应一些方法:
/**
* 按下标读取
*/
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
/**
* 复制
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
/**
* 序列化
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
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();
}
}
}
ArrayList 扩容
ArrayList的扩容机制简单来讲,就是我们在添加元素前,会检查现有的容量(容量就是elementData.length())够不够,不够,容量就扩大为原来的1.5倍,够了,就直接添加。扩容的具体细节由ArrayList中如下几个私有函数完成:
- ensureCapacityInternal(int minCapacity)
- calculateCapacity(Object[] elementData ,int)
- ensureExplicitCapacty(int)
- grow(int )
- hugeCapacity(int)
1. ensureCapacityInternal(int minCapacity)
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
minCapacity指需要的最小容量,如使用add增加1个元素,minCapacity=size+1,使用addAll增加n个元素,minCapacity=size+n。
肯定的讲,只要添加元素(add addAll),添加前,一定会检查是否需要扩容,add调用ensureCapacityInternal检查是否需要扩容,addAll直接调用ensureExplicitCapacity是否需要扩容。
2. calculateCapacity(Object[] elementData ,int)
private static int calculateCapacity(Object[] elementData, int minCapacity) { //elementData就是我们存放列表的地方(值或者引用)
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
当我们使用无参构造函数ArrayList<>()初始化类时,会执行this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA,根据上面DEFAULTCAPACITY_EMPTY_ELEMENTDATA字段定义,刚刚初始化时,elementData的容量为0,当第一次add时,直接扩容为10(即elementData.length = 10),后面当minCapacity大于10时,都不会扩容,当minCapacity大于10时,newCapacity = element.length()>>1 + elementData.length(),即扩容成原容量的1.5倍。
3. ensureExplicitCapacty(int)
private void ensureExplicitCapacity(int minCapacity) {
modCount++; //当结构可能会改变时,modCount也修改了,多线程环境下,某个线程迭代器可能检测到,执行fail-fast机制
// overflow-conscious code
if (minCapacity - elementData.length > 0) //需要的最小容量都大于现有容量,那就需要扩容,这条语句会有数值溢出风险
grow(minCapacity);
}
4. grow(int )
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) //当扩容达到允许的容量上限后,(MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 ),不在按照1.5倍扩容,也不按需扩容
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
5. hugeCapacity(int)
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? //如果大于 MAX_ARRAY_SIZE,直接扩充成Integer.MAX_VALUE,否则直接扩为 MAX_ARRAY_SIZE
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
小结
首次利用无参初始化ArrayList示例的时候,列表容量为0,当加入第一个元素时,立即扩容为10,以后每次需要的最小容量达到当前容量时,每次按当前容量1.5倍扩容,当扩容扩到一定程度时候,理想情况下,会出现两种情况:第一种扩容后,新容量比原容量小,则执行按需扩容(有多少元素,就开多大空间),第二种扩容后,容量超过容量上限,如果原容量小于容量上限就直接扩充成容量上限,如果原容量已经大于容量上限,直接扩成Integer.MAX_VALUE。
值得考虑的是,这种"弹性"的扩容机制在极大数据量下肯定是不安全的,比如,我们目前已经扩到Integer.MAX_VALUE,然后我们在add一个元素(minCapacity = -2147483648),从ensureCapacityInternal走一套下来,我们发现添加不进去,原数组也不会变,这无可厚非,但是在大数据情况下,这种扩容是否是完备的呢?即我们在不超过Integer.MAX_VALUE时,是否总能安全存放数据?我认为扩容后只要不溢出且不达到最大容量,都是安全的,否则只要溢出数据就不安全了。
ArrayList 迭代
ArrayList中实现了两种迭代器:Iterator,ListIterator。我们可以通过iterator() ,listIterator() 函数得到这两种两种迭代器。
迭代器源码浅析
-
iterator()源码
public Iterator iterator() { return new Itr(); //返回一个匿名Itr()类 }
-
listIterator()源码
public ListIterator listIterator() { return new ListItr(0); //从第0位置开始迭代 }
-
listIterator(int index)源码
public ListIterator listIterator(int index) { if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index); return new ListItr(index); //从index位置开始迭代 }
-
ArrayList Itr内部类源码
private class Itr implements Iterator { int cursor; // index of next element to return 将要返回的元素的索引 int lastRet = -1; // index of last element returned; -1 if no such 上一个已返回的元素的索引,如果还没开始迭代,初始值为-1 int expectedModCount = modCount; //初始化expectedModCount = modCount,检测列表结构是否被改变 Itr() {} //无参空的构造函数 public boolean hasNext() { return cursor != size; //cursor == size 表示目前已到列表尾部 }
@SuppressWarnings("unchecked") public E next() { checkForComodification(); //检测 expectedModCount 是否等于modCount,相等则认为目前无其他线程在修改此列表结构,否则直接抛异常 int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; //让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.this.remove(lastRet); cursor = lastRet; lastRet = -1; // expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { //得到Itr()类后,我们可以iterator.forEachRamaining(System.out::println) Objects.requireNonNull(consumer); //由于Comsumer函数式接口中的accept没有返回值,所以不会对this.elementData做任何修改 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(); } }
注意到使用iterator()返回的Itr()匿名内部类,只可以对列表元素进行遍历和移除操作。想要得到列表的索引,一定要先执行next()
-
ArrayList ListItr内部类源码
private class ListItr extends Itr implements ListIterator { //继承了内部类Itr(),[cursor,lasRet,hasNext,next,remove,forEachRemaining] ListItr(int index) { super(); cursor = index; }
public boolean hasPrevious() { return cursor != 0; } public int nextIndex() { //返回cursor位置 return cursor; } public int previousIndex() { //返回cursor-1位置 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]; } 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; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } }
调用listIterator(int index),可以得到ListIterator迭代器,这是一个双向迭代器,cursor既可以往前,也可以往后。Iterator是一个顺序单想迭代器,只能往后。不管时ListIterator还是Iterator在想要得到索引之前,一定要先执行next()或previous(),利用listIterator迭代器可以对列表实现增删改查操作,Iterator迭代器可以对列表实现remove操作,通过源码可以发现这些操作实质都是对原列表直接操作的。
其他
- int hashCode() 和 boolean equals(Object o)
这两个方法都是直接继承自AbstractList,ArrayList没有重写Override 者两个方法
hashCode()
public int hashCode() {
int hashCode = 1;
for (E e : this) //遍历每个元素,计算一个总的hashCode
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode()); //e.hashCode()的计算根据e的类型
return hashCode;
}
若 e 是 Integer类型,则e.hashCode就是数字本身,如 Integer i = 20,i.hashCode()就是20,其他类型如Double Float String hashCode的实现方法各异。
equals(Object o)
public boolean equals(Object o) {
if (o == this) //指向同一个引用,直接认为相等
return true;
if (!(o instanceof List)) //如果都不是实现了List接口的类或子类,直接认为不相等
return false;
//是实现了List接口类或其子类
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator(); //先强制转换迭代器的类型
while (e1.hasNext() && e2.hasNext()) { //一次迭代每个元素,看是否相等
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext()); //其中一个较短的已经比较完,且两个列表都相等,若其中一个列表还有下个元素,则返回false,否则返回true
}
equals 和 hashCode 具有相同的语义,即为 a.equals(b)为 true,则 a.hashCode() 一定等于 b.hashCode(),所以这两个方法在类中一定是同时重写的。
补充
ArrayList其他API
void forEach(Consumer<? super E> action) //遍历每个元素,对每个元素做accept操作
Spliterator<E> spliterator() //to do list
boolean removeIf(Predicate<? super E> filter) //移除列表中符合filter动作的元素
void replaceAll(UnaryOperator<E> operator)
void sort(Comparator<? super E> c) //根据c的规则排序
Object[] toArray() //列表转数组,是数组和列表之间的桥梁
<T> T[] toArray(T[] a) //列表转数组,返回数组的运行时类型是指定数组的运行时类型。