上一篇文章我們對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);
}
運作結果:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISM1UTM1YzM0EjNwYDM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
第二個方法: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的疊代器進行詳細的解析,請持續關注