带你走进Java集合_ArrayList源码深入分析2

上一篇文章我们对ArrayList的属性、构造方法、增删改查方法进行了详细的了解,也解读了为什么在多线程下ArrayList不能作为共享变量的原因,本篇文章主要介绍ArrayList的两个功能相似的方法。加入我们定义list

第一个方法:removeAll

public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);----------------------❶
        return batchRemove(c, false);-------------------❷
    }

❶就是判断给出的集合是否为null,如果为null,则会抛出NullPointerException异常。

removeAll方法就是list移除所有包含在c中的元素

举例说明:

 List<Integer>list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        Collection<Integer>c = new ArrayList<Integer>();
        c.add(2);
        c.add(4);
        c.add(6);
        list.removeAll(c);
        for(Integer i:list){
            System.out.println(i);
        }

运行结果:


第二个方法:retainAll

public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, true);------------------❸
    }

retainAll方法就是list移除所有不包含在c中的元素

举例说明:

List<Integer>list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        Collection<Integer>c = new ArrayList<Integer>();
        c.add(2);
        c.add(4);
        c.add(6);
        list.retainAll(c);
        for(Integer i:list){
            System.out.println(i);
        }

运行结果:


我们从两个示例中可以看出,removeAll就是两个集合的差集,retainAll就是两个集合的交集。

我们知道这两个方法的作用后,我们就开始从源码中分析是怎样实现的,因为removeAll和retainAll两个方法最终的实现都是调用batchRemove方法,区别就是第二个参数不同,removeAll传递的是false,retainAll传递的是true。

removeAll-->list.batchRemove(c,false)
retainAll-->list.batchRemove(c,true)

接下来我们看看batchRemove到底怎样实现的

private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;
        boolean modified = false;
        try {
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)---------------❹
                    elementData[w++] = elementData[r];
        } finally {
            if (r != size) {
                System.arraycopy(elementData, r,elementData, w,size - r);
                w += size - r;
            }
            if (w != size) {
                // 下面的循环,把元素赋值null,让GC回收
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }
从batchRemove的源码中,我们大概可以看出主要的逻辑在
for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)---------------❹
                    elementData[w++] = elementData[r];

我们以上面两个示例带入的方法帮大家去理解(c包含2、4,6)

对于removeAll:

1)当r=0时,elementData[0]=1,c.contains(1)==false  等式成立  elementData[w++]=elementData[0]=1 此时w=1

2)当r=1时,elementData[1]=2,c.contains(2)==false  等式不成立 此时w=1

3)当r=2时,elementData[2]=3,c.contains(3)==false  等式成立  elementData[w++]=element[2]=3,此时w=2

4)当r=3时,elementData[3]=4,c.contains(4)==false  等式不成立 此时w=2
5)当r=4时,elementData[4]=5,c.contains(5)==false  等式成立  elementData[w++]=element[4]=5,此时w=3

6)当r=5时,elementData[5]=6,c.contains(6)==false  等式不成立 此时w=3

所以循环结束后局部变量elementData={1,3,5,4,5,6},此时r=6,w=3,然后在看finally中的代码

if (r != size) {
    System.arraycopy(elementData, r, elementData, w, size - r);
    w += size - r;
}

上面的这段代码是判断是否出现过异常,如果出现了异常,则r一定等于size,如果,如果没有任何的问题,程序是进不去这段代码的,那么现在elementData={1,3,5,4,5,6},而我们知道,removeAll后的list应该是{1,3,5},所以接下来的代码就应该是把4,5,6删除掉,Java源码是这样实现的:

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;
            }

我们知道此时w=3,一定会进入这个方法,所以从下标为3开始将元素赋值null,被GC回收,至此elementDatas还有1,3,5三个元素。所以就证明把包含c的元素去掉了。

对于retainAll:

1)当r=0时,elementData[0]=1,c.contains(1)==true  等式不成立 此时w=0
2)当r=1时,elementData[1]=2,c.contains(2)==true  等式成立  elementData[w++]=elementData[1]=2 此时w=1
3)当r=2时,elementData[2]=3,c.contains(3)==true  等式不成立 此时w=1
4)当r=3时,elementData[3]=4,c.contains(4)==true  等式成立  elementData[w++]=element[3]=4,此时w=2
5)当r=4时,elementData[4]=5,c.contains(5)==true  等式不成立 此时w=3
6)当r=5时,elementData[5]=6,c.contains(6)==true  等式成立  elementData[w++]=element[5]=6,此时w=3

所以retainAll和removeAll恰好相反。此时elementDatas={2,4,6,4,5,6},通过从下标3开始将元素赋值null,被GC回收,至此elementDatas还剩2,4,6三个元素

从上面的分析,大家是不是非常清楚removeAll和retainAll的作用了,removeAll可能用的比较多,而retainAll用的相对少点,如果不了解内部的机制,即使现在记住了两个的作用和区别,但是一段时间后就会忘掉,这样分析后是不是清晰多了。

总结:

1)removeAll是取两个集合的差集

2)retainAll是取两个集合的交集

本篇文章就先写到这,接下来会对ArrayList的迭代器进行详细的解析,请持续关注



















猜你喜欢

转载自blog.csdn.net/shaotianqiang/article/details/80594227