1. 写在前面
接着上一篇文章,我们继续来分析jdk ArrayList的源码
上一篇文章我们看了向ArrayList中添加元素的底层实现,对应的,jdk也提供了从ArrayList中删除元素的操作,在看代码之前我先抛出几个问题大家可以一起思考下:
- 将元素从数组中删除底层是怎么实现的?
- 元素删除后 数组会缩容吗?
2. remove源码解读
ArrayList提供了两种移除元素的方式,一种是根据下标,另外一种根据对应的目标元素。
2.1 根据下标
/**
* 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);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
- rangeCheck(index)
这个方法用于检查索引是否在有效范围内。如果索引超出范围,则抛出 IndexOutOfBoundsException 异常。源代码如下:
/**
* 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));
}
-
modCount++
modCount 是 ArrayList 的一个字段,用于记录对列表结构的修改次数(如添加、删除操作)。它用于支持 fail-fast 机制,检测并发修改。 -
获取旧值
获取并保存要删除的元素,用于方法返回。elementData(index) 是一个私有方法,用于根据索引获取元素。 -
计算需要移动的元素数量
int numMoved = size - index - 1;
计算从指定索引到最后一个元素之间的元素数量,这些元素需要向左移动一个位置。
- 移动元素
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
如果需要移动的元素数量大于 0,则使用 System.arraycopy 方法将 index+1 位置后的所有元素向左移动一个位置,覆盖掉被删除元素的位置。
- 清除最后一个元素
elementData[--size] = null; // clear to let GC do its work
将数组中最后一个有效元素位置置为 null,以便垃圾回收器(GC)能够回收该对象。同时,减少 size 表示列表的当前大小。
2.2 根据目标元素
/**
* 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
* <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns <tt>true</tt> if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return <tt>true</tt> if this list contained the specified element
*/
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;
}
- 检查元素是否为 null
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;
}
}
- 这个方法首先检查传入的元素 o 是否为 null。
- 如果 o 为 null,则遍历列表,查找第一个 null 元素的位置,并调用 fastRemove(index) 方法删除该元素,然后返回 true。
- 如果 o 不为 null,则遍历列表,查找第一个等于 o 的元素的位置,并调用 fastRemove(index) 方法删除该元素,然后返回 true。
- 辅助方法 fastRemove
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
}
fastRemove 方法用于在指定索引处快速删除元素,并将后续元素左移以填补空缺
- 增加 modCount 以支持 fail-fast 机制。
- 计算需要移动的元素数量,并使用 System.arraycopy 方法将这些元素左移一个位置。
- 将数组中最后一个有效元素位置置为 null,以便垃圾回收器(GC)能够回收该对象。
- 减少 size 表示列表的当前大小。