ArrayList原理学习(三)

3 篇文章 0 订阅
2 篇文章 0 订阅

ArrayList原理学习(三)

ArrayList删除指定索引范围的元素方法

protected void removeRange(int fromIndex, int toIndex) {
    // 当前集合修改次数++
    modCount++;
    // 获取到toIndex后存在元素个数
    int numMoved = size - toIndex;
    // 将toIndex后存在的元素复制到formIndex索引后,即将后面元素向前移动toIndex-fromIndex
    System.arraycopy(elementData, toIndex, elementData, fromIndex,
                     numMoved);
    // 获取到元素移动后的新数组元素长度
    int newSize = size - (toIndex-fromIndex);
    // 通过循环将新数组元素长度后的所有元素置空,方便垃圾回收
    for (int i = newSize; i < size; i++) {
        elementData[i] = null;
    }
    // 将新长度赋值给原长度
    size = newSize;
}

ArrayList删除指定集合中包含元素

public boolean removeAll(Collection<?> c) {
    // Objects类的带泛型的静态方法,具体作用就是判断对象
    // 是否为null,如果是null就报空指针异常,不为null直接返回这个对象
    Objects.requireNonNull(c);
    // 删除本集合中包含的参数数组中的元素
    return batchRemove(c, false);
}

ArrayList删除指定集合中不包含元素(取交集)

public boolean retainAll(Collection<?> c) {
 // Objects类的带泛型的静态方法,具体作用就是判断对象
 // 是否为null,如果是null就报空指针异常,不为null直接返回这个对象
    Objects.requireNonNull(c);
 // 删除本集合中不包含的参数数组中的元素
    return batchRemove(c, true);
}

ArrayList中batchRemove()方法

private boolean batchRemove(Collection<?> c, boolean complement) {
    // 申明局部变量将原数组值赋予局部变量
    final Object[] elementData = this.elementData;
    // 声明属性,用于循环
    int r = 0, w = 0;
    boolean modified = false;
    try {
        // 通过循环判断第一个满足条件的元素位置
        // 当complement为false时,通过contains方法
        // 将不存在于c集合的元素以0索引开始存储到elementData集合中
        // 并将存在于c集合的元素以0索引开始存储到elementData集合中
        for (; r < size; r++)
            if (c.contains(elementData[r]) == complement)
                elementData[w++] = elementData[r];
    } finally {
        // Preserve behavioral compatibility with AbstractCollection,
        // even if c.contains() throws.
        // 当r的长度不等于size时,将elementData的r索引后的元素移动到w索引位置之后
        if (r != size) {
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            // 最后一个元素索引位置赋予w                 
            w += size - r;
        }
        // 当w索引值不等于size时
        if (w != size) {
            // 将w索引及之后所有元素值置空,方便垃圾回收
            for (int i = w; i < size; i++)
                elementData[i] = null;
            // 元素修改测试为删除元素个数    
            modCount += size - w;
            // 当前元素个数为w值
            size = w;
            // 返回true
            modified = true;
        }
    }
    // 失败为false
    return modified;
}

ArrayList迭代器

// 声明迭代器
public Iterator<E> iterator() {
        return new Itr();
}
// 迭代器的内部实现
 private class Itr implements Iterator<E> {
        // 光标,默认值为0
        int cursor;  
        // 返回最后一个元素的索引,默认为-1
        int lastRet = -1; 
        // 迭代开始时记录当前集合修改次数,用于后继判断
        int expectedModCount = modCount;
        // 判断当前迭代器是否循环完成,即光标是否等于原集合的元素长度
        public boolean hasNext() {
            return cursor != size;
        }
        @SuppressWarnings("unchecked")
        // 取值方法
        public E next() {
            // 用于校验当前集合是否进行了修改通过对比记录的修改次数和现在集合修改次数是否
            // 相等做判断,相等表示没有修改该集合,不相等抛出异常
            checkForComodification();
            // 将光标值赋予i
            int i = cursor;
            // 当i值大于size值时,抛出异常,说明已经没有元素了
            if (i >= size)
                throw new NoSuchElementException();
            // 把集合存储数据数组的地址赋值给该方法的局部变量 
            Object[] elementData = ArrayList.this.elementData;
            // 进行判断,如果条件满足就会促发并发修改异常
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            // 光标值加一    
            cursor = i + 1;
            // 将i值给lastRet,并取出相关元素
            return (E) elementData[lastRet = i];
        }
        // 迭代器内部删除方法,
        // 当执行迭代器内部删除时,本质上还是使用的时集合内部删除
        public void remove() {
            // 当最后一个元素的索引记录值小于0时抛出异常
            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) {
            // 对参数进行判null处理
            Objects.requireNonNull(consumer);
            // 得到相关的集合长度
            final int size = ArrayList.this.size;
            // 将光标值赋予i
            int i = cursor;
            // 对i进行校验
            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();
        }
}

注意事项:当要删除的元素在集合的倒数第二个位置的时候,不会参数并发修改异常。

原因:是因为在调用hasNext方法的时候,光标的值和集合的长度一样,那么就会返回false,因此就不会在调用next方法获取集合的元素,既然不会调用next方法。那么底层就不会产生并发修改异常。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值