从上篇Java集合的初步认识文章中,我们了解到 Collection 是Java集合的一大鼻祖,Collection 接口中定义了集合类的基本操作方法。在了解ArrayList之前,我们先来看看它的祖先Collection。
Collection接口中的方法
源码中 Collection 的定义
public interface Collection<E> extends Iterable<E> {
Collection 接口中的方法
返回类型 | 方法 | 描述 |
---|---|---|
int | size() | 返回集合中的元素的个数 |
boolean | isEmpty() | 若集合中不含有元素,返回true |
boolean | contains(Object o) | 若集合中含有某指定元素,返回true |
boolean | containsAll(Collection<?> c) | 若集合中包含指定集合中的所有元素,则返回true |
Iterator<E> | iterator() | 返回集合中元素的迭代器 |
Object[] | toArray() | 把集合中元素放入数组中,返回数组 |
<T> T[] | toArray(T[] a) | 返回包含此集合中所有元素的数组;返回数组的运行时类型是指定的数组的运行时类型 |
boolean | add(E e) | 添加指定类型的元素到集合中 |
boolean | addAll(Collection<? extends E> c) | 将指定集合中的所有元素添加到这个集合 |
boolean | remove(Object o) | 移除集合中指定的元素 |
boolean | removeAll(Collection<?> c) | 移除此集合包含指定集合的所有元素 |
default boolean | removeIf(Predicate<? super E> filter) | 删除满足条件的元素(本接口已实现方法) |
boolean | retainAll(Collection<?> c) | 仅保留包含在指定集合中的这个集合中的元素 |
void | clear() | 清空集合 |
boolean | equals(Object o) | 将指定的对象与此集合比较 |
int | hashCode() | 返回此集合的哈希值 |
default Spliterator<E> | spliterator() | 创建此集合中的元素的 Spliterator(本接口已实现方法) |
default Stream<E> | stream() | 返回一个序列 Stream与集合的来源(本接口已实现方法) |
default Stream<E> | parallelStream() | 返回一个可能并行 Stream与集合的来源(本接口已实现方法) |
ArrayList
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
从源码中ArrayList的定义,我们可以看出ArrayList是AbstractList的一个子类,同时又实现了 List、RandomAccess、Cloneable、Serializable 接口,使其具有快速随机访问
、可克隆
、可序列化
的功能。
ArrayList上层结构
通过上面的图,我们可以清晰的了解ArrayList的上层结构,ArrayList是List接口的一大实现类,也是List接口的最常用的实现类,下面就来详细的分析ArrayList。
ArrayList的源码分析
成员变量
// 默认大小 10
private static final int DEFAULT_CAPACITY = 10;
// ArrayList 中实际元素的个数
private int size;
// ArrayList 存储元素的数组对象
transient Object[] elementData;
// 用于空实例的共享空数组实例(构造方法初始化对象是使用给定的大小)
private static final Object[] EMPTY_ELEMENTDATA = {};
// 共享的空数组实例用于默认大小的空实例(构造方法初始化对象时使用默认大小)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// ArrayList 默认最大的容量大小(设置Integer的最大值 - 8 是因为要空出8的大小给数组对象的 _length)
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
为什么初始化ArrayList对象时要分两个数组对象初始化呢?源代码是这样解释的 We distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when first element is added
是用来区分初始化ArrayList对象的方式是通过无参构造还是有参构造,这样做的目的是为了后面的扩容。
构造方法
// 无参构造方法,使用默认的空数组实例对象(大小为默认的大小 10)
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 有参构造方法,用于初始化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);
}
}
// 有参构造方法,为空的时候也是使用空数组实例对象;不为空时直接拷贝传入的对象元素放入到ArrayList中,大小为传入对象的大小
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对象,底层使用的是静态的 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
Object对象实例数组;而另外两个有参构造方法目的是用来初始化用户想要大小的ArrayList对象,当用户给定的大小为 0 的时候,底层使用静态的 EMPTY_ELEMENTDATA
Object对象实例数组;当这两种有参方法传入的参数大小不为 0 的时候,底层使用的是给定大小的动态的 Object 数组对象。这样做的目的是为了后面的扩容策略。
主要的方法源码解析
add(E e) 添加单个元素
// add 方法的主入口
public boolean add(E e) {
// 主要解决AraayList大小问题的方法主入口
ensureCapacityInternal(size + 1); // Increments modCount!!
// 把传入的元素添加到ArrayList中
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 判断传入的大小是否大于ArrayList的大小
private void ensureExplicitCapacity(int minCapacity) {
// 每次操作都计数一次,该字段来源于ArrayList的父类AbstractList抽象类,主要作用是为了避免遍历的时候修改ArrayList对象
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
// ArrayList 的扩容的方法
grow(minCapacity);
}
// 判断ArrayList的大小,若是无参方法创建的实例就取默认大小与传入大小的最大值,否则直接取传入值
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
经过对ArrayList源代码的分析,我们可以知道每次添加元素之前都会确认ArrayList容量的大小,再去size++。在确认ArrayList容量大小的过程中,我们可以看到源代码是先去对容量大小参数minCapacity做一个粗略的判断,在方法 calculateCapacity
中我们可以看到是做了分底层数组对象处理,若为DEFAULTCAPACITY_EMPTY_ELEMENTDATA
(也就是无参构造函数实例化出来的默认的静态数组对象)对象,则取默认大小(10)与传入大小的最大值,若是有参构造实例化出来的对象,则直接取传入的minCapacity
值;然后在ensureExplicitCapacity
方法中去做是否进行扩容操作的判断。扩容的实际操作是在 grow
方法中,我们接下来看看ArrayList是怎样扩容的。
grow(int minCapacity) 扩容操作
private void grow(int minCapacity) {
// overflow-conscious code
// 记录原ArrayList的容量大小
int oldCapacity = elementData.length;
// 使用为位运算的方式进行扩容,扩为原来大小的 1.5 倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 判断扩容后的大小与传入的大小比较,若还小,就直接让新容量大小为传入的大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 若扩容后的大小超过 Integer.MAX_VALUE - 8 的大小,则使用 Integer.MAX_VALUE
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 将数据拷贝到扩容后的 ArrayList 中
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 取 Integer.MAX_VALUE 值做ArrayList的容量大小
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
对ArrayList的扩容方法的源码分析,我们可以了解到ArrayList的默认一次扩容大小为 1.5
倍原来的容量大小;源码对扩容后的大小又做了进一步的判断,若扩容后的大小还不满足用户的需求,则直接把ArrayList的容量大小设为用户需要的大小(传入的大小),最后对最终的容量大小又进一步判断,判断是否超过 Integer.MAX_VALUE - 8 = 2147483639
的大小,超过则直接给 Integer.MAX_VALUE = 2147483647
的大小,若还超过,那么对不起,直接报错O(∩_∩)O哈哈~。
为什么ArrayList的最大容量大小设置为 Integer.MAX_VALUE - 8
?关于这个问题,细心地看官应该能注意到,我在列出ArrayList成员变量的时候简单的解释了这个问题;其实也不难理解,数组是一个对象,内存在存储对象的时候,不但要存储对象中的元素,还要存储对象的头信息,对象的头信息不可能会超过 8 ,所以ArrayList的默认最大的容量就设置为 Integer.MAX_VALUE - 8
;但后面又为什么把Integer的最大值给ArrayList的容量呢?这里其实是想最大限度的提高内存的使用率,对象的头信息不可能超过 8 ,也就是说对象的头信息有可能只需要占 4 的大小就可以了,有进一步占据空间的可能性。
add(int index, E element) 指定位置上插入一个元素
public void add(int index, E element) {
// 对传入的下标进行有效检测判断
rangeCheckForAdd(index);
// 尝试改变 ArrayList 容量的大小
ensureCapacityInternal(size + 1); // Increments modCount!!
// 调用底层的数组拷贝方法,空出下标为 index 的位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 把元素添加到指定下标的位置
elementData[index] = element;
// ArrayList 的实际元素的大小 +1
size++;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
addAll(Collection<? extends E> c) 添加一个集合到ArrayList中
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
addAll(int index, Collection<? extends E> c) 从ArrayList指定下标开始添加一个集合元素
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
// 尝试改变 ArrayList 容量的大小
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
// 腾出空间来存储传入的集合元素
System.arraycopy(elementData, index, elementData, index + numNew, numMoved);
// 将传入的集合元素添加到 ArrayList 指定位置
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
分析完ArrayList的添加操作,我们解下来分析下ArrayList的删除操作。
remove(int index) 移除指定下标的元素
public E remove(int index) {
// 检测给定的下标是否在规定的范围内
rangeCheck(index);
// 修改ArrayList操作的计数器
modCount++;
// 取出指定下标的元素,用于返回
E oldValue = elementData(index);
int numMoved = size - index - 1;
// 指定下标不是ArrayList的最后一个元素的下标时,调用底层数组的拷贝方法
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
// 将原先ArrayList的最后以一个元素设置为null,便于被回收,保证不浪费内存空间
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
private void rangeCheck(int index) {
// 只做了向上的越界,没有做向下的越界的原因在于给定下标index < 0,ArrayList在调用底层数组拷贝时,不会做元素的移动
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
removeRange(int fromIndex, int toIndex) 移除一个范围的元素
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
remove(Object o) 移除指定的元素
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;
}
fastRemove(int index) 快速移除
// 快速移除方法,所谓开始就是不做任何判断,只专注做移除元素的业务操作,其限制判断在别的方法做了限制,保证了其传入的 index 的有效性
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
}
removeAll(Collection<?> c) 移除与指定集合元素相匹配的元素
public boolean removeAll(Collection<?> c) {
// 判断传入的集合是否为空,调用 Object 类的不为空的方法
Objects.requireNonNull(c);
// 批量移除
return batchRemove(c, false);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
// 定义不可变的变量指向外部的 elementData 的内存地址
final Object[] elementData = this.elementData;
// r 表示读. w表示写
int r = 0, w = 0;
boolean modified = false;
try {
// for 循环保留与传入的集合c中的元素不匹配的数据,存储在elementData(方法里的变量)
for (; r < size; r++)
// complement 是一个标记, true表示保留, false表示移除
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
// 抛出异常的情况下,移除抛异常之前的与传入参数的元素相匹配的数据
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
// 没有抛异常,正常遍历结束,移除 w 后面的所有元素(置null)
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;
}
retainAll(Collection<?> c) 保留与指定集合元素相匹配的元素
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
// true,保留与之相匹配的元素,方法在上面代码框内
return batchRemove(c, true);
}
通过分析 removeAll
的源代码,我们ArrayList的方法设计非常解耦、通用,它通过一个complement
标记,省略了极大的重复代码,这个标记被 removeAll
和 retainAll
两个方法所使用,前者是移除
与传参集合中元素相匹配的数据,后者是保留
与传参集合中元素相匹配的数据。
clear() 清空所有
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
removeIf(Predicate<? super E> filter) 移除满足条件的元素
@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
// 使用BitSet对象记录要移除元素的下标
final BitSet removeSet = new BitSet(size);
// 用于判别对ArrayList的改动次数是否一致,主要就是防止多线程修改
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];
// 若集合元素满足指定的规则,则将BitSet中对应下标位置的值设为true
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
// 判断其他线程是否对该对象集合做了改动
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
// newSize 为过滤之后集合的实际大小
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
// nextClearBit 返回设置为false的第一个位置的索引(下标)
i = removeSet.nextClearBit(i);
// 保存不符合断言条件的元素,变相地移除了满足条件的元素
elementData[j] = elementData[i];
}
// 将newSize后面的元素全部置null
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
// 判断其他线程是否对该对象集合做了改动
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
removeIf
方法是对Collection接口方法的重写,源码也说了在执行该方法的过程中,抛出任何异常都会中断操作,不会对ArrayList做任何变更性操作
,方法是根据过滤条件去移除满足条件的数据,里面运用到 BitSet 这个在后续讲解Set系列的时候回去详细的讲解,这里只针对运用到的方法做简单地讲解。removeIf
方法的核心就在于对BitSet的运用,分别用到了set() 和 nextClearBit() 方法,set()方法是将满足过滤条件的下标设置为true,nextCLearBit()方法是取出为false的下标。
举个例子:
ArrayList集合元素下标情况:[0, 1, 2, 3, 4, 5, 6]
满足断言过滤条件的下标情况:[3, 5, 6]
for循环调用nextCLearBit()得出不满足过滤条件的下标:[0, 1, 2, 4]
通过分析ArrayList的移除操作的源代码,我们可以知道ArrayList的移除操作其实是很简单的,就是普通的数组移除元素的思想,先对传入的参数做有效性判定,然后就是找到要删除元素的下标,再调用底层的数组相应拷贝方法,移动ArrayList数组,最后就做相应的返回。
replaceAll(UnaryOperator operator) 替换满足条件的元素
从方法参数可以看出参数是一个函数式接口,这就意味着参数可以使用Lambda表达式
@Override
@SuppressWarnings("unchecked")
public void replaceAll(UnaryOperator<E> operator) {
// 不为空校验
Objects.requireNonNull(operator);
// List结构被修改的次数
final int expectedModCount = modCount;
final int size = this.size;
// 没有被多线程修改结构
for (int i=0; modCount == expectedModCount && i < size; i++) {
// 按照 lamdba 表达式做相应的操作
elementData[i] = operator.apply((E) elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
// UnaryOperator 接口
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
/**
* Returns a unary operator that always returns its input argument.
*
* @param <T> the type of the input and output of the operator
* @return a unary operator that always returns its input argument
*/
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
// List 里面的方法
default void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final ListIterator<E> li = this.listIterator();
while (li.hasNext()) {
li.set(operator.apply(li.next()));
}
}
示例:
@Test
public void replaceTest(){
List<Integer> list = createData(); // 生成 0 - 9 的数字List
list.iterator().forEachRemaining(i -> System.out.print(String.valueOf(i) + "、"));
// 把元素为 3 的改为 13
list.replaceAll(i -> i == 3 ? 13 : i);
System.out.println();
list.iterator().forEachRemaining(i -> System.out.print(String.valueOf(i) + "、"));
}
输出显示:
0、1、2、3、4、5、6、7、8、9、
0、1、2、13、4、5、6、7、8、9、
sort(Comparator<? super E> c) 按条件排序
该方法的参数也是一个函数式接口,条件就可以通过这个接口去设定
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
示例:
@Test
public void sortTest(){
List<Integer> list = createData();
list.iterator().forEachRemaining(i -> System.out.print(String.valueOf(i) + "、"));
System.out.println();
list.sort((a, b) -> b.compareTo(a)); // 置为倒序
list.iterator().forEachRemaining(i -> System.out.print(String.valueOf(i) + "、"));
}
输出结果:
0、1、2、3、4、5、6、7、8、9、
9、8、7、6、5、4、3、2、1、0、
iterator 迭代器
iterator() 用于迭代输出
public Iterator<E> iterator() {
return new Itr();
}
从源码中,可以看出,调用 iterator
方法,其实就是new 出来一个内部类对象,我们接下来就来看看此 Itr
内部类。
ArrayList 的内部类
Itr 内部类
private class Itr implements Iterator<E> {
成员变量
// 用于指向下一个要返回的值
int cursor; // index of next element to return
// 用于返回值的下标(在实际操作中,会返回下标[lastRet + 1]的值)
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
构造方法
Itr() {}
hasNext() 判断后面是否还存在元素
public boolean hasNext() {
return cursor != size;
}
next() 取出元素
public E next() {
// 检测是否有其他线程对此对象进行操作
checkForComodification();
int i = cursor;
// 判断cursor后面是否还有元素
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
// 进一步保证不会越界
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
// 返回curson移动之前下标对应的元素,同时把lastRet指向curson移动之前下标
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
这里解释的可能不太好,大家意会下,就是两个指针,一前一后的指向相邻的两个元素。
remove() 移除元素
public void remove() {
// 对 lastRet 做判断,防止没有经过 next 就开始删除元素
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
// 调用外部类的 remove 方法,直接把指定下标(lastRet)后面的元素网前移动
ArrayList.this.remove(lastRet);
// 将cursor指向lastRet,因为lastRet后面所有的元素都往前移动一位,cursor的值也就必须往前移
cursor = lastRet;
// 将lastRet的值重新设置为起始值,防止在遍历的过程恶意进行无限循环删除
lastRet = -1;
// 将期望修改值重新赋值为修改值,目的就是为了能够进行多次操作,因为经过一次删除操作,modCount加了1,在next()方法开始之前,就对这两个进行了一次相等校验,不重新赋值,下一次调用next就会报错,这将会和普通的for一样,在遍历的过程中不能对ArrayList进行修改性操作
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
通过对内部类Itr的 hasNext
、next
、remove
三个方法的源代码的解读,我们终于知道为什么iterator迭代器支持遍历的同时对ArrayList对象进行修改性的操作,原因就在于内部类 remove
方法的 expectedModCount = modCount
语句,它可以使得删除操作对修改次数的校验不会抛异常。
forEachRemaining(Consumer<? super E> consumer) 迭代器遍历
@Override
@SuppressWarnings("unchecked")
// 该方法重写了 Iterator 接口中的方法,思维逻辑很简单,我这就不过多的解释
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();
}
该方法支持流操作,可以对元素做用户自己想做的操作,很灵活,建议大家能多使用这个方法。
ListItr 内部类
public ListIterator<E> listIterator() {
return new ListItr(0);
}
private class ListItr extends Itr implements ListIterator<E> {
该内部类继承了 Itr 内部类,所以它具备 Itr 所有操作;但由于 Itr 类只能做遍历中删除,不能做遍历中修改和插入,为了弥补这一缺陷,就出现了 ListItr 内部类。
构造方法
ListItr(int index) {
super();
cursor = index;
}
三个基础方法
// 判断当前元素前面是否有元素
public boolean hasPrevious() {
return cursor != 0;
}
// 获取当前的下标
public int nextIndex() {
return cursor;
}
// 获取前一个数的下标
public int previousIndex() {
return cursor - 1;
}
操作方法
previous() 获取前一个元素
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];
}
set(E e) 将指定下标的值设置为参数e
public void set(E e) {
// 这个判断就限制了该方法必须经过一次next或previous方法且lastRet 大于等于0
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
add(E e) 添加一个元素
public void add(E e) {
checkForComodification();
// 思想与remove是一样的,这里只不过是添加,所以应该cursor往后移动
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
SubList 内部类
生成方法:
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
static void subListRangeCheck(int fromIndex, int toIndex, int size) {
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > size)
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
}
private class SubList extends AbstractList<E> implements RandomAccess {
成员变量
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
构造函数
SubList(AbstractList<E> parent, int offset, int fromIndex, int toIndex) {
// 这里指的父类不是继承的那种关系,是指外部类,说父类只是便于说明
this.parent = parent; // 指向父类
this.parentOffset = fromIndex; // 父类的偏移量
this.offset = offset + fromIndex; // 子类的偏移量
this.size = toIndex - fromIndex; // 子类的实际大小
this.modCount = ArrayList.this.modCount; // 修改次数
}
观察这个构造方法,这个子List不是单独生成的,它是指向父List的,所以它内部的方法操作的结果必定会影响到父List的元素,反之亦然。
方法
SubList的内部类中的方法与外部类ArrayList的方法的实现思想是一样的,只不过是添加了一些偏移量而已;由于有太多重复的,前面分析ArrayList的的时候大部分都已经说过,只要能理解前面的,这里的也自然能理解,我就一一在解读一遍,只挑几个方法来解读。
重要:SubList内部类要慎用,尽量不要对SubList做修改操作。
add(int index, E e) 在指定位置添加一个元素
public void add(int index, E e) {
rangeCheckForAdd(index);
checkForComodification();
// 直接对父List的指定位置添加一个元素
parent.add(parentOffset + index, e);
this.modCount = parent.modCount;
this.size++;
}
remove(int index) 移除指定位置的元素
public E remove(int index) {
rangeCheck(index);
checkForComodification();
// 直接移除父List指定位置的一个元素
E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;
this.size--;
return result;
}
从添加和删除中,我们可以看到所有修改操作,都会直接去操作父List,所以会直接影响到父List的数据结构。SubList内部类里面也有 iterator 方法,该方法是创建一个 ListIterator 内部类(SubList里面的内部类),器实现思想都是一样的,我这里就不再去解析了,有兴趣的可以自己去看源码,很好理解的,一看就能懂得。
iterator()
public Iterator<E> iterator() {
return listIterator();
}
public ListIterator<E> listIterator(final int index) {
checkForComodification();
rangeCheckForAdd(index);
final int offset = this.offset;
return new ListIterator<E>() { ... }
总结
- ArrayList底层结结构为 Object 数组,其默认容量为 10,默认扩容大小为 1.5 倍,一次扩容不成功则
newCapacity > MAX_ARRAY_SIZE ? Integer.MAX_VALUE : MAX_ARRAY_SIZE
- ArrayList 有三种初始化对象,分别是无参构造产生的静态Object数组
DEFAULTCAPACITY_EMPTY_ELEMENTDATA
,有参构造参数是空数据产生的静态Object数组EMPTY_ELEMENTDATA
,有参构造参数是有数据产生的动态 Object 数组。 - ArrayList 在for循环中不能多次修改操作,可以在Iterator里面做多次修改操作。
- ArrayList 有四大
Iterator
、ListIterator
、SubList
、ArrayListSpliterator
内部类。Iterator
只支持向后遍历,ListIterator
支持双向遍历,SubList
要慎用,ArrayListSpliterator
把List均匀分割成几部分。 - ArrayList本质是数组,所以它的查找速率很快,而插入删除,性能会比较低。