简介
ArrayList继承于AbstractList,实现了List接口,是一个长度可变的集合,提供了增删改查的功能。
ArrayList实现了RandomAccess接口,可以对元素进行快速访问。
ArrayList实现了Serializable接口,可以对ArrayList进行序列化。
ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。
ArrayList底层是通过数组来实现的,当向数组中添加元素时,数组的容量大小不足则会扩容。
成员变量
/**
* Default initial capacity.
* 默认初始容量
* 创建集合时没有传递集合大小则使用默认大小(10)
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空数组对象
* 该空数组对象则是在创建的时候传递容量大小为0时创建的
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 空数组对象
* 该空数组对象则是在创建的时候未传递容量大小创建的
* 默认容量大小为10
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 数组对象
*/
transient Object[] elementData;
/**
* 集合长度
*/
private int size;
/**
* 扩容的数组最大的容量大小
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
构造函数
- ArrayList():创建一个空的数组对象,在第一次添加元素的时候会将数组的容量大小扩到10
- ArrayList(int initialCapacity):创建一个指定初始容量大小的数组
- ArrayList(Collection<? extends E> c):根据指定的元素集合构建一个新的数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
//校验初始容量是否大于0
if (initialCapacity > 0) {
//初始容量大于0则创建一个指定容量大小的object数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//初始容量等于0则创建一个空的object数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
//初始容量小于0则抛出异常
throw new IllegalArgumentException("Illegal Capacity:"+initialCapacity);
}
}
public ArrayList(Collection<? extends E> c) {
//获取集合中的数组
Object[] a = c.toArray();
if ((size = a.length) != 0) {
//校验集合类型
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
//集合不是arrayList类型则使用拷贝方法将转换的数组中的数据拷贝到新的Object数组中
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
//创建一个空的数组对象
elementData = EMPTY_ELEMENTDATA;
}
}
添加操作
- add(E e):将元素添加到数组的末尾
- add(int index, E element):将元素添加到数组中指定的索引位置
- addAll(Collection<? extends E> c):将元素集合添加到当前数组中的末尾
- addAll(int index, Collection<? extends E> c):.将元素集合添加到数组中指定的索引位置
public boolean add(E e) {
//校验将数组中的元素追加到当前数组中是否需要扩容,如果需要扩容则直接进行扩容
ensureCapacityInternal(size + 1);
//将添加的元素放置size索引位置(索引从0开始,刚好size则是下一个添加的元素索引位置),元素添加完成之后并将集合长度size加1。
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
//检查指定的索引是否大于集合的长度size或小于0
rangeCheckForAdd(index);
//校验将数组中的元素追加到当前数组中是否需要扩容,如果需要扩容则直接进行扩容
ensureCapacityInternal(size + 1);
//将源数组中的元素拷贝到目标数组中去
//此处则是将index位置开始的元素向后移动一位
System.arraycopy(elementData, index, elementData, index + 1, size - index);
//将元素添加到指定的索引位置
elementData[index] = element;
size++;
}
示例:
elementData = [1,a,2,3,g,5]
idnex = 2
从源数组(第1个elementData)中的指定起始索引位置2(index)开始拷贝,拷贝元素个数为4(size-idnex),并将拷贝的元素放置到目标数组(第2个elementData)中的指定起始索引位置(index+1),最终数组元素为[1,a,2,2,3,g,5]。
public boolean addAll(Collection<? extends E> c) {
//获取指定集合中的数组
Object[] a = c.toArray();
//数组长度
int numNew = a.length;
//校验将数组中的元素追加到当前数组中是否需要扩容,如果需要扩容则直接进行扩容
ensureCapacityInternal(size + numNew);
//元素拷贝
//将数组a中的元素拷贝到elementData数组的末尾
System.arraycopy(a, 0, elementData, size, numNew);
//更新集合长度
size += numNew;
return numNew != 0;
}
public boolean addAll(int index, Collection<? extends E> c) {
//检查元素追加的索引位置是否超出集合长度
rangeCheckForAdd(index);
//获集合中的数组
Object[] a = c.toArray();
//数组长度
int numNew = a.length;
//校验将数组中的元素追加到当前数组中是否需要扩容,如果需要扩容则直接进行扩容
ensureCapacityInternal(size + numNew);
//当前数组中的元素插入到当前数组中,当前数组中需要移动的元素个数
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew, numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
//更新当前集合长度
size += numNew;
return numNew != 0;
}
-
情况一:numMoved大于0
numMoved大于0则先调用if语句里面的数组拷贝方法预留出空间再次调用数组拷贝方法将指定的集合元素添加到预留的空间去 -
情况二:numMoved小于等于0
numMoved小于等于0说明不需要移动当前集合中的元素位置,直接将指定的元素集合追加到当前数组末尾
示例:
elementData = [张三,李四,王五]
index = 1
numMoved = 2(elementData.size - index)
a = [赵六,田七]
numMoved大于0则会先调用if判断中的数组拷贝方法,从源数组(第1个elementData)中的起始索引位置1(index)开始拷贝元素,拷贝元素个数为2(numMoved),并将拷贝的元素放置到目标数组(第2个elementData)中的指定索引起始位置3(index+numNew),最终拷贝的数组为[张三,李四,王五,李四,王五],
再次调用数组拷贝方法则从源数组(a)中的起始索引位置0开始开始拷贝元素,拷贝元素个数为2(numNew(a数组的长度)),将拷贝的元素放置到目标数组(elementData)中的指定索引起始位置1(index),则是将第1次数组拷贝的结果数组中的索引1和2的元素替换为a数组中的元素,最终结果为[张三,赵六,田七,李四,王五]。
删除操作
- remove(int idnex):删除指定索引位置的元素
- removeIf(Predicate<? super E> filter):根据条件删除满足该条件的元素
- removeAll(Collection<?> c):批量删除与集合c中相同的元素
- retainAll(Collection<?> c):批量删除与集合c中不相同的元素
- clear():清空数组中所有元素
public E remove(int index) {
//检查索引是否超出当前数组最大长度
rangeCheck(index);
//数组修改次数加1
modCount++;
//获取被删除的索引位置的元素
E oldValue = elementData(index);
//获取需要移动的元素数量(被删除的元素后面的所有数据则需要往前移动)
int numMoved = size - index - 1;
if (numMoved > 0)
//如果被删除的索引元素后面的数据数量大于0则需要将元素往前移动
System.arraycopy(elementData, index+1, elementData, index, numMoved);
//将数组拷贝后末尾重复的元素置为空
elementData[--size] = null;
//返回被删除的元素
return oldValue;
}
- 情况一:numMoved小于等于0
需要移动的元素个数小于等于0则说明被删除的元素在数组末尾,则直接将末尾的元素置为null - 情况二:numMoved大于0
需要移动的元素大于0则使用数组拷贝方法将被删除的元素后面的所有元素向前移动
示例:
elementData = [张三,李四,王五,赵六,田七]
index = 1
numMoved = 3(elementData.size - index -1)
从源数组(第1个elementData)中起始索引位置2(index+1)开始拷贝元素,拷贝元素个数为3(numMoved),将拷贝的元素放置目标数组(第2个elementData)中起始索引位置1(index),最终拷贝结果为 [张三,王五,赵六,田七,田七],然后再将最终结果数组末尾中的重复元素置为null,此处将元素置为null与调用方法添加null元素不相同,调用方法添加null的时候size也会增加,从而让null也变成了一个元素,而直接将元素置为null的时候size没有增加,则不会将null看成是数组中的一个元素。
public boolean removeIf(Predicate<? super E> filter) {
//校验参数是否为null
Objects.requireNonNull(filter);
//已删除数量
int removeCount = 0;
//创建一个当前集合大小的位集,用于存放被删除的元素索引
final BitSet removeSet = new BitSet(size);
//数组预期的修改次数
final int expectedModCount = modCount;
//集合长度
final int size = this.size;
//每次循环的时候判断数组有没有被修改并且当前循环的索引是否小于集合长度,一旦大于集合的长度或数组被修改则停止循环
for (int i=0; modCount == expectedModCount && i < size; i++) {
//获取当前遍历到的索引元素
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
//判断当前索引元素是否满足条件
if (filter.test(element)) {
//将元素索引添加到待删除的位集中
removeSet.set(i);
//删除数量自增
removeCount++;
}
}
//数组被修改则抛出异常
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
//被删除的元素是否大于0
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
//删除之后剩余的元素个数
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
//获取下一个比特位为0的索引
/**
* 示例: [0,1,0,1,0] BitSet
* 0 1 2 3 4
* i = 0 获取到的索引0
* i = 1 获取到的索引为2
* i = 2 获取到的索引为2
* i = 3 获取到的索引为4
*/
i = removeSet.nextClearBit(i);
//使用被删除的元素的后一位元素替换删除元素
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++) {
//将数组中末尾重复的元素置为null
//进了当前循环方法则说明有元素被删除
//被删除的元素后面的元素则需要向前移动
//而末尾中的元素向前移动的时候则会多一个重复元素,则需要将末尾中重复的元素置为null
elementData[k] = null;
}
//更新集合长度
this.size = newSize;
//数组被修改则抛出异常
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
//数组修改次数加1
modCount++;
}
示例:
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>(10);
list.add(1);
list.add(3);
list.add(2);
//删除集合中等于3的元素
list.removeIf((a)->{
if(3 == a){
return true;
}
return false;
});
}
public boolean removeAll(Collection<?> c) {
//校验集合是否为空
//集合为空则直接报空指针异常
Objects.requireNonNull(c);
//false表示删除elementData数组中与集合c中相同的元素
return batchRemove(c, false);
}
public boolean retainAll(Collection<?> c) {
//校验集合是否为空
//集合为空则直接报空指针异常
Objects.requireNonNull(c);
//true表示删除elementData数组中与集合c中不相同的元素
return batchRemove(c, true);
}
public void clear() {
//数组修改次数加1
modCount++;
//遍历将数组中的所有元素置为null
for (int i = 0; i < size; i++)
elementData[i] = null;
//将集合长度置为0
size = 0;
}
batchRemove方法是ArrayList中的一个私有方法,该方法用于批量删除数组中的元素,入参为元素集合和一个布尔类型的标识,该方法被removeAll(Collection<?> c) 和retainAll(Collection<?> c)调用。
batchRemove方法完整代码:
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 {
if (r != size) {
System.arraycopy(elementData, r, elementData, w, size - r);
w += size - r;
}
if (w != size) {
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
1.
//当前集合的数组对象
Object[] elementData
//r 当前集合读取的索引位置
//w 当前集合写入的索引位置
int r = 0, w = 0;
//当前集合是否被修改
modified
2.
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
complement == false时,当集合c中的元素不包含r索引位置的元素时则使用r索引位置的元素替换w索引位置的元素,r索引位置的元素不变,r、w自增,反之r自增,w不变。
示例:
elementData = [1,2,3,4,5,6,7,8,9]
c = [a,3,f,b,7,9]
1.r=0,w=0; 集合c中不包含索引为0的元素1,则使用r索引为0的元素替换w索引为0的元素,w++,替换后的elementData = [1,2,3,4,5,6,7,8,9]
2.r++, r=1,w=1;集合c中不包含索引为1的元素2,则使用r索引为1的元素替换w索引为1的元素,w++,替换后的elementData = [1,2,3,4,5,6,7,8,9]
3.r++,r=2,w=2;集合c中包含索引为2的元素3,则不替换元素, elementData = [1,2,3,4,5,6,7,8,9]
4.r++,r=3,w=2;集合c中不包含索引为3的元素4,则使用r索引为3的元素替换w索引为2的元素,w++,替换后的elementData = [1,2,4,4,5,6,7,8,9]
5.r++,r=4,w=3;集合c中不包含索引为4的元素5,则使用r索引为4的元素替换w索引为3的元素,w++,替换后的elementData = [1,2,4,5,5,6,7,8,9]
6.r++,r=5,w=4;集合c中不包含索引为5的元素6,则使用r索引为5的元素替换w索引为4的元素,w++,替换后的elementData = [1,2,4,5,6,6,7,8,9]
7.r++,r=6,w=5;集合c中包含索引为6的元素7,则不替换元素, elementData = [1,2,4,5,6,6,7,8,9]
8.r++,r=7,w=5;集合c中不包含索引为7的元素8,则使用r索引为7的元素替换w索引为5的元素,w++,替换后的elementData = [1,2,4,5,6,8,7,8,9]
9.r++,r=8,w=6;集合c中包含索引为8的元素9,则不替换元素,elementData = [1,2,4,5,6,8,9,8,9]
3.
if (r != size) {
System.arraycopy(elementData, r, elementData, w, size - r);
w += size - r;
}
r != size 在正常情况下r跟szie是相等的,只有在第2部分中的代码执行异常时才会不相等,在不相等的情况下则会将r索引后的所有元素向前移动并更新集合写入的索引。
System.arraycopy方法参数说明,从源数组(第一个elementData)中r索引位置开始拷贝,拷贝的长度为size-r,将拷贝的元素放置目标数组(第二个elementData)中的起始位置w。
4.
if (w != size) {
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
w != size时则说明有元素需要进行删除,从w索引位置开始遍历将包含w索引位置后的所有元素置为null,在第2部分中的示例最终的数组为[1,2,4,5,6,8,9,8,9],w=6,则将数组中的9,8,9元素置为null,元素删除完成之后更新集合的修改次数(当前集合的修改次数加上删除元素的个数)和长度并返回true。
获取操作
- get(int index):获取指定索引位置上的元素
- indexOf(Object o):返回元素从左到右第一次匹配到的相同元素的索引位置
- lastIndexOf(Object o):返回元素从右到左第一次匹配到的相同元素的索引位置
- toArray():获取当前集合的数组对象
- toArray(T[] a):获取指定类型的数组对象
- clone():获取当前集合的副本
- iterator():获取集合的迭代器
- subList(int fromIndex, int toIndex):根据起始索引位置获取新的子集合(创建的子集合中不包含结束索引位置的元素),对子集合操作都会同步到父集合中,子集合中的操作方法跟父集合中的操作方法大致相同
public E get(int index) {
//检查索引是否超出当前数组最大长度
rangeCheck(index);
//获取指定索引位置的元素
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
public int indexOf(Object o) {
//校验指定元素是否为null
if (o == null) {
//指定元素为null
//遍历数组
for (int i = 0; i < size; i++)
//校验数组中每一个索引位置的元素是否为null
if (elementData[i]==null)
//元素为null则返回当前元素所在的索引位置
return i;
} else {
for (int i = 0; i < size; i++)
//校验数组中每一个索引位置的元素是否和指定元素相同
if (o.equals(elementData[i]))
//相同则返回元素所在的索引位置
return i;
}
//指定元素在数组中不存在
return -1;
}
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
public Object[] toArray() {
//将当前数组对象拷贝到一个新的数组对象中并返回
return Arrays.copyOf(elementData, size);
}
public <T> T[] toArray(T[] a) {
//校验元素存放的数组长度是否小于当前集合长度
if (a.length < size)
//小于,则将当前集合中的元素拷贝到指定类型的数组中
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
//大于等于,则将当前集合中的元素拷贝到指定类型的数组中
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
//如果a数组长度大于当前集合的长度则在a[size]处放置一个null
//这个null值可以判断出null后面已经没有当前集合中的元素了
a[size] = null;
return a;
}
public Object clone() {
try {
//返回当前集合对象的副本
ArrayList<?> v = (ArrayList<?>) super.clone();
//将当前集合对象的元素拷贝到副本中
v.elementData = Arrays.copyOf(elementData, size);
//设置副本修改次数
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
public Iterator<E> iterator() {
return new Itr();
}
//迭代器实现类
private class Itr implements Iterator<E> {
//当前元素的索引
int cursor;
//最左元素的索引
int lastRet = -1;
//预期修改次数
int expectedModCount = modCount;
Itr() {}
//校验当前元素的索引是否超出集合最大长度
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();
//索引+1,等待获取下一个索引的元素
cursor = i + 1;
//返回最左边索引的元素
return (E) elementData[lastRet = i];
}
//删除集合中的元素
public void remove() {
//校验最左元素索引是否小于0
//remove方法不能在一开始直接调用,必须在next方法调用之后才能调用
if (lastRet < 0)
throw new IllegalStateException();
//校验当前集合最近是否被修改过
checkForComodification();
try {
//调用remove方法删除指定索引位置的元素
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) {
//校验参数是否为null
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();
}
}
public List<E> subList(int fromIndex, int toIndex) {
//检查起始索引位置是否在正确的范围内
subListRangeCheck(fromIndex, toIndex, size);
//创建指定索引位置的子集合
return new SubList(this, 0, fromIndex, toIndex);
}
修改操作
- trimToSize():根据集合的长度去掉数组中多余的null来释放空间,该方法不会将调用方法手动添加的null值去掉,只会将数组在分配空间的时候提前预先分配好的空间置为的null去掉
- set(int index, E element):替换指定索引位置上的元素
- sort(Comparator<? super E> c):.集合排序,按指定的比较器对集合进行排序,如果传入的比较器为null则使用默认排序(从小到大)
- replaceAll(UnaryOperator operator):按指定的一元表达式替换数组中所有的元素
- ensureCapacity(int minCapacity):数组手动扩容,当前扩容方法和add中的扩容方法相似
- forEach(Consumer<? super E> action):对数组中所有的元素按指定的消费行为方法执行
public void trimToSize() {
//数组修改次数加1
modCount++;
//校验当前集合长度是否小于数组长度
if (size < elementData.length) {
//当前集合长度是否等于0,如果集合长度等于0则将空数组对象赋与数组,空数组对象中不包含任何内容
//如果集合长度不等于0,则使用拷贝方法将数组以集合的长度拷贝到原数组中
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
public E set(int index, E element) {
//检查索引是否超出当前数组最大长度
rangeCheck(index);
//获取被替换的索引元素
E oldValue = elementData(index);
//将替换的元素添加到被替换的元素索引位置上
elementData[index] = element;
//返回被替换的元素
return oldValue;
}
E elementData(int index) {
return (E) elementData[index];
}
public void sort(Comparator<? super E> c) {
//预期集合的修改次数
final int expectedModCount = modCount;
//对集合进行排序
Arrays.sort((E[]) elementData, 0, size, c);
//当前集合修改次数是否与预期的修改次数相同
if (modCount != expectedModCount) {
//不相同则说明在排序期间其它线程对当前集合进行了修改
throw new ConcurrentModificationException();
}
//数组修改次数加1
modCount++;
}
示例:
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>(10);
list.add(1);
list.add(3);
list.add(2);
//写法一
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
//写法二
list.sort((a,b)->{
return a.compareTo(b);
});
}
public void replaceAll(UnaryOperator<E> operator) {
//校验参数是否为null
Objects.requireNonNull(operator);
//预期集合的修改次数
final int expectedModCount = modCount;
//集合长度
final int size = this.size;
//每次循环的时候判断数组有没有被修改并且当前循环的索引是否小于集合长度,一旦大于集合的长度或数组被修改则停止循环
for (int i=0; modCount == expectedModCount && i < size; i++) {
//对指定索引中的元素执行运算并将运算的结果替换到当前索引中
elementData[i] = operator.apply((E) elementData[i]);
}
//数组被修改则抛出异常
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
//数组修改次数加1
modCount++;
}
示例:
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>(10);
list.add(1);
list.add(3);
list.add(2);
//对集合中所有的元素进行加1操作
list.replaceAll(a->a+1);
//将集合中所有的元素替换成4
list.replaceAll(a->4);
}
public void ensureCapacity(int minCapacity) {
//校验当前数组是否在创建的时候未指定初始容量而创建的空数组对象
//如果创建的时候未指定初始容量则使用默认的初始容量 DEFAULT_CAPACITY
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
? 0
: DEFAULT_CAPACITY;
//校验指定的扩容的容量大小是否大于最小默认的初始容量大小
if (minCapacity > minExpand) {
//确认数组是否需要扩容,需要扩容则直接进行数组扩容
ensureExplicitCapacity(minCapacity);
}
}
//判断数组是否需要进行扩容
private void ensureExplicitCapacity(int minCapacity) {
//数组修改次数加1
modCount++;
//校验数组容量是否已经达到最大
if (minCapacity - elementData.length > 0)
//数组扩容
grow(minCapacity);
}
//扩容
private void grow(int minCapacity) {
//旧数组对象的容量
int oldCapacity = elementData.length;
//(oldCapacity >> 1) 右移一位获取到旧数组对象容量的一半容量大小
//新数组对象的容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
//扩容失败则使用实际元素的容量大小
newCapacity = minCapacity;
//校验是否超出最大的数组扩容容量大小
if (newCapacity - MAX_ARRAY_SIZE > 0)
//超出数组最大扩容容量大小
//hugeCapacity 获取最大扩容容量大小
newCapacity = hugeCapacity(minCapacity);
//将旧数组对象中的数据复制到新扩容的数组对象中
elementData = Arrays.copyOf(elementData, newCapacity);
}
//如果扩容的容量到达了数组定义的最大容量大小,则需要获取更大的容量
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
//校验最小容量是否大于数组最大扩容容量大小
//大于则返回int最大值
//小于则返回数组最大扩容容量大小
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
public void forEach(Consumer<? super E> action) {
//校验参数是否为null
Objects.requireNonNull(action);
//预期集合的修改次数
final int expectedModCount = modCount;
//当前集合的数组
final E[] elementData = (E[]) this.elementData;
//集合长度
final int size = this.size;
//每次循环的时候判断数组有没有被修改并且当前循环的索引是否小于集合长度
//一旦大于集合的长度或数组被修改则停止循环
for (int i=0; modCount == expectedModCount && i < size; i++) {
//调用消费行为方法执行
action.accept(elementData[i]);
}
//数组被修改则抛出异常
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
示例:
public static void main(String[] args) {
List<String> list = new ArrayList<String>(10);
list.add("张三");
list.add("李四");
list.add("王五");
list.forEach((str)->{
System.out.println(str);
});
}