咱們知道,對於集合(Collection)都有一個抽象方法removeAll(Collection<?> c)!php
可是你可知道,在集合數據比較多的狀況下, ArrayList.removeAll(Set)
的速度遠遠高於ArrayList.removeAll(List)
!css
我簡單測試了一下,從1百萬數據中remove掉30萬數據,前者須要0.031秒,後者須要1267秒!vue
這不是危言聳聽,你們感興趣能夠去實測一下。java
先看一下大概的類結構圖: ios
從圖中能夠看到,圖中相關的集合類(HashSet
、
LinkedList
、
ArrayList
),除了
ArrayList
本身實現了
removeAll()
方法外,其餘兩個集合都是藉助父類(或超父類)的Iterator迭代器進行刪除。
也許這也是爲什麼ArrayList
的removeAll()
方法對於不一樣類型的參數,表現出「不同凡響」的緣由吧~!c++
咱們再來細看ArrayList類的removeAll()
方法的實現。es6
爲節省各位看官的時間,具體代碼我就不貼出來,貼一個僞代碼吧,更容易閱讀:web
如:list.removeAll(subList);
//1.將list中不刪除的元素移到數組前面(咱們知道ArrayList的底層是數組實現)
int w=0; //w爲不刪除和要刪除的分界線
for(var value in 該list的底層數組){
if(!subList.contain(value)){
該list的底層數組[w]=value;
w++;
}
}
//2.將w後面的元素所有置爲null
xxx
複製代碼
其中,咱們能夠看到影響速率關鍵的一步:subList.contain(value)
!npm
因此速率的差別,其實也就在於參數集合.contain()
方法的差別~vim
實現很簡單,即調用indexOf(),一個一個地遍歷查找。最壞時間複雜度爲
O(總數據量)
。
咱們知道,HashSet的底層是HashMap,所以,實際也就是調用
map.containKey()
方法。
你們都知道,HashMap的查找速度很是快!
所以,到這裏,咱們也就解釋題目的問題。同時也知道了,在數據量比較大的的狀況下,使用arrayList.removeAll(subList)
時,能夠更改成:
subList
封裝爲HashSet
:arrayList.removeAll(new HashSet(subList))
arrayList
改成LinkedList
:new LinkedList(arrayList).removeAll(subList)
都說到這兒了,不聊聊map的一點東西,也說不過去了。
先上圖:
咱們知道,HashMap的底層是數組
+鏈表
。而containsKey()
底層其實也就是getEntry(key)
,而後判斷該Entry是否爲null,來達到目的!
在JDK1.8中,getEntry()
即getNode()
。另外,get(key)
方法的底層一樣也是(e=getEntry(key))!=null?e.value:null
。
說多了,咱們迴歸正題。
圖上,最頂行爲一個數組,而每列是一個個鏈表。
每一個元素put進來須要放在哪兒,大概須要這些步驟:
索引位置 = (數組.size-1) & hash(key.hashcode())
- 以前版本是將上面的位運算
&
換成了取餘%
,效果都同樣,都是爲了防止hashcode值超出數組的長度。不過位運算效率確定是大於取餘的。
- 科普:
a & b = c
,那麼c<=min(a,b)
,所以獲得的索引始終小於數組.size-1
,至於爲什麼會小於等於c<=min(a,b)
?
- 如:4 & 8 = 00000100 & 00001000,相同位置進行
與運算
,與運算
是二者均真才爲真!所以咱們看最小的那個數(00000100
),任何數與它進行與運算
,前面5位都不可能爲1,那麼結果只能小於等於4~
- 另外注意,上面用了一個hash()方法,是爲了讓全部key的hash保持均勻,爲何要這樣作呢?
- 舉個例子,你重寫了hashcode方法,返回都是1。最後hashmap在存儲這類對象時,全都放到同一個索引位置去了!
- 注意:若是數據過大,JDK1.8會自動切換鏈表爲紅黑樹實現
所以,就containsKey()
而言,最壞的時間複雜度爲:O((總數據量/數組長度)*最長鏈表長度)
而這個數組長度
到底有多長?鏈表有多長?它們和數據量成一個什麼關係呢?
咱們須要簡單探究一下HashMap的實現:
由圖可知,數組長度
通常都是大於總數據量(負載因子<=1時)。所以最壞時間複雜度≈O(最長鏈表長度)。
那麼鏈表長度有多長?
設想一下,數組長度
>=總數據量,那麼最好狀況下(各數據的hash均勻分佈),可能一個鏈表就一個元素,即時間複雜度可能爲O(1)!
至少大多狀況下,鏈表長度都不會太長,除非你就是那個重寫hashcode,始終返回1的人!