ArrayList 的源码分析 之 remove(int index)

本文深入剖析了ArrayList中元素删除的实现机制,包括检查索引范围、更新修改计数及元素移动过程。详细介绍了rangeCheck方法防止下标越界,modCount字段确保迭代器的fail-fast行为,以及如何通过System.arraycopy进行元素左移。
/**
 * 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)

/**
 * 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));
}

此方法的作用是把要删除的index 的值与当前集合的大小作比较,如果大于等于当前集合大小直接,抛出下标越界的异常

第二处:

/**
 * The number of times this list has been <i>structurally modified</i>.
 * Structural modifications are those that change the size of the
 * list, or otherwise perturb it in such a fashion that iterations in
 * progress may yield incorrect results.
 *
 * <p>This field is used by the iterator and list iterator implementation
 * returned by the {@code iterator} and {@code listIterator} methods.
 * If the value of this field changes unexpectedly, the iterator (or list
 * iterator) will throw a {@code ConcurrentModificationException} in
 * response to the {@code next}, {@code remove}, {@code previous},
 * {@code set} or {@code add} operations.  This provides
 * <i>fail-fast</i> behavior, rather than non-deterministic behavior in
 * the face of concurrent modification during iteration.
 *
 * <p><b>Use of this field by subclasses is optional.</b> If a subclass
 * wishes to provide fail-fast iterators (and list iterators), then it
 * merely has to increment this field in its {@code add(int, E)} and
 * {@code remove(int)} methods (and any other methods that it overrides
 * that result in structural modifications to the list).  A single call to
 * {@code add(int, E)} or {@code remove(int)} must add no more than
 * one to this field, or the iterators (and list iterators) will throw
 * bogus {@code ConcurrentModificationExceptions}.  If an implementation
 * does not wish to provide fail-fast iterators, this field may be
 * ignored.
 */
protected transient int modCount = 0;

modCount :这个值简单的来说用来记录结合被修改次数,以及用来判定集合是否被“非法”修改

第三处:判断numMoved字段是不是最后一个元素,如果最后的元素直接把那个位置赋值为null,不进行数据搬移工作

### Java `ArrayList` 源码详解 #### 一、类定义与继承关系 `ArrayList` 是实现了 `List` 接口的一个动态数组实现。其内部维护了一个对象数组作为存储容器,允许随机访问元素并支持高效的迭代操作[^1]。 ```java public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8683452581122892189L; } ``` #### 二、底层数据结构 `ArrayList` 的核心在于它使用了 Object 数组来保存所有的元素: ```java transient Object[] elementData; // non-private to simplify nested class access ``` 当创建一个新的 `ArrayList` 实例时,默认容量为空,即不分配任何空间给 `elementData`;只有在第一次添加元素时才会初始化默认大小为 10 的数组[^2]。 #### 三、扩容机制 每当向列表中追加新项而当前容量不足以容纳这些新增项目时,就会触发一次扩容过程。具体来说,会按照现有长度的一半增加新的容量,并将旧的数据复制到更大的内存区域中去[^3]。 ```java private void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; ... elementData = Arrays.copyOf(elementData, newCapacity); } ``` #### 四、常用方法解析 ##### 添加元素 (`add(E e)`) 此方法用于在列表末尾插入指定元素。如果必要的话还会调用前面提到过的 `grow()` 函数来进行自动扩展。 ```java public boolean add(E e) { ensureCapacityInternal(size + 1); elementData[size++] = e; return true; } ``` ##### 获取元素 (`get(int index)`) 通过索引快速定位所需位置上的值,时间复杂度 O(1),因为可以直接计算出目标地址。 ```java public E get(int index) { rangeCheck(index); return elementData(index); } ``` ##### 删除元素 (`remove(int index)`) 移除特定下标的成员并将后续部分向前移动一位填补空缺,平均情况下需要线性扫描整个序列完成调整工作。 ```java 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; } ``` #### 五、遍历方式 除了传统的基于索引循环外,还可以利用增强型for-each语句或是 Iterator 来高效地处理大量连续排列的对象实例集合。 ```java // 使用Iterator遍历 Iterator<String> it = list.iterator(); while(it.hasNext()){ String str = it.next(); } // 或者更简洁的方式 for(String s : list){ } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值