ArrayList源码解析之remove,removeIf

本文详细解析ArrayList的remove()、removeAll()和JDK1.8新特性removeIf()。remove()包括移除指定位置元素和指定对象;removeAll()通过batchRemove()移除指定集合元素;removeIf()则根据Predicate过滤并删除元素。

remove() 方法用于删除集合中的元素。本篇解析ArrayList的remove(),removeAll(),以及JDK1.8中新增的removeIf()方法。

一、remove()

remove()方法有两个:
1、删除集合中指定位置元素的 remove(int index) 方法。
2、删除指定的、在集合正序遍历中第一次出现元素的 remove(Object o) 方法。

    // 根据下标删除元素
    public E remove(int index) {
        // 检查下标是否越界
        rangeCheck(index);
        
        modCount++;
        E oldValue = elementData(index);
        
        // 需要移动元素的个数
        int numMoved = size - index - 1;
        if (numMoved > 0)
        	// 将elementData数组index+1位置(包含index+1)以后numMoved个元素向前移动一位
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

    // 删除指定的、在正序遍历中第一次出现的元素
    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;
    }

    /*
     * 该方法与remove(int index)方法差异在于该方法不用检测下标是否越界
     * 虽然没有下标越界检查,但index的取值范围为 0 <= index < size
     */
    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()

removeAll(Collect<?> c) 删除集合中所有在指定集合c中存在的元素,主要方法为私有batchRemove()方法。

    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }
    
    private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        // 执行结果
        boolean modified = false;
        try {
            /*
             * 如果目标 c集合中包含elementData[]中的元素,
             * 则用此时r位置的元素(不存在c集合)替换w位置的元素(存在于c中)
             * 图一演示过程
             */
            for (; r < size; r++)
                // boolean complement = false
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            /*
             * 如果for循环执行过程中,contains()方法抛出异常
             * 此时需要整理elementData数组
             * 将抛出异常时r位置(包含r)以后的元素全部向前移动r-w位
             */ 
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);

                // 此时集合size值
                w += size - r;
            }
            /*
             * 如果无异常抛出,w下标(包含w)以后的元素,都已被复制到w下标之前
             * 之后的元素做null值处理
             */
            if (w != size) {
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                // 移除成功后,w为此时的size
                size = w;
                modified = true;
            }
        }
        return modified;
    }

在这里插入图片描述

三、removeIf()

removeIf(Predicate<? super E> filter)方法是JDK1.8新增方法,作用是按照一定的规则过滤集合中的元素,示例:

import java.util.ArrayList;

public class RemoveIfDemo {

    public static void main(String[] args) {
        
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        
        // 移除大于5的元素
        list.removeIf(it -> it > 5);
        
        System.out.println(list);
        // [0, 1, 2, 3, 4, 5]
        
    }

}

源码解析

    /*
     * JDK1.8新增方法,作用是按照一定的规律过滤集合中的元素
     */
    @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);
        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) {
            // 过滤之后集合新的size
            final int newSize = size - removeCount;
            // 循坏的目的在于移除满足规则的元素
            for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
                /*
                 * nextClearBit(int index)返回设置为false的第一个位置的索引
                 * 发生在指定的起始索引之后。
                 * 如果集合元素为[0,1,2,3,4],BitSet中存储需要移除的下标为[1,3]
                 * 则在for循坏中 i = nextClearBit(i)返回的i值为0 2 4 
                 * 所以此时i表示不符合规则元素的下标
                 * 可见图二
                 */
                i = removeSet.nextClearBit(i);
                elementData[j] = elementData[i];
            }
            // 从newSize(包含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;
    }

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值