ArrayList的增加扩容啥的小伙伴一定很清楚了,那些也比较好理解,对于removeAll方法,大家想一想,以自己的思维去写一个removeAll方法,是怎样
写呢?
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
这是jdk8给的removeall方法,我们只看参数,它接受的是一个实现了Collection接口的集合,意为从原有的list里,将传入的集合在list里含有的元素全部移除。
我试着自己想了一下,我很笨,我只要实现这个功能,根本不考虑时间复杂度,我就会把列表挨个遍历,拿到列表遍历的然后又拿去遍历集合,元素相等时,我就把此位置设为null,两层for循环,理解简单但是消耗大。
其实jdk的removeAll方法在遍历上差不多一样(本身contains方法就包含遍历)但是不同的是当元素存在相等时,对数据的处理,接下来我们上源码
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 {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
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;
}
}
return modified;
}
我们看下属性
elementData:list存放元素的数组
参数Collection<?> c:要去除的元素的集合
complement:一个参数,始终为false,先不用在意,在比较中起辅助作用
r,w则是遍历的下标
我们来看判断条件if (c.contains(elementData[r]) == complement),由于complement始终为false,所以这句话含义就为:
如果在要去除的元素的集合不含有当前list的数组r下标的元素,我们则进行if里面的操作,文字描述生疏,我们来图片展示一下
借用这位博主的一张图
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
思路描述就是,在边进行判定的时候,边把数组的元素前移,
w这个下标,在c.contains为false时它才会自增,它并不代表的是重复元素的下标,而是在每次c.contains不成立时,标记这个位置,将r下标的某个元素移到此位置来,因为这时候就表明r下标的这个元素时不需要去除的,这样就形成了边判定边移动数组的行为。总有来说,只要有一个元素不符合,后面c.contains为true的时候,都会往前移动一个位置
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;
}
这部分
跟着上面的例子,w6,从原数组124679789的 下标6开始,将原数组大于下标6以后的数置null
modCount 修改的次数+3,原数组的长度修改为6。