Create, Read, Update, and Delete.
那我也把這些 API 分為這四大類:

下面具體來看:
增:
boolean add(E e);
add() 方法傳入的資料類型必須是 Object,是以當寫入基本資料類型的時候,會做自動裝箱 auto-boxing 和自動拆箱 unboxing。
還有另外一個方法 addAll(),可以把另一個集合裡的元素加到此集合中。
boolean addAll(Collection<? extends E> c);
删:
boolean remove(Object o);
remove()是删除的指定元素。
那和 addAll() 對應的,
自然就有removeAll(),就是把集合 B 中的所有元素都删掉。
boolean removeAll(Collection<?> c);
改:
Collection Interface 裡并沒有直接改元素的操作,反正删和增就可以完成改了嘛!
查:
查下集合中有沒有某個特定的元素:
boolean contains(Object o);
查集合 A 是否包含了集合 B:
boolean containsAll(Collection<?> c);
還有一些對集合整體的操作:
判斷集合是否為空:
boolean isEmpty();
集合的大小:
int size();
把集合轉成數組:
Object[] toArray();
以上就是 Collection 中常用的 API 了。
在接口裡都定義好了,子類不要也得要。
當然子類也會做一些自己的實作,這樣就有了不同的資料結構。
那我們一個個來看。
List
List 最大的特點就是:有序,可重複。
看官網說的:
An ordered collection (also known as a sequence).
Unlike sets, lists typically allow duplicate elements.
這一下把 Set 的特點也說出來了,和 List 完全相反,Set 是 無序,不重複的。
List 的實作方式有 LinkedList 和 ArrayList 兩種,那面試時最常問的就是這兩個資料結構如何選擇。
對于這類選擇問題:
一是考慮資料結構是否能完成需要的功能;
如果都能完成,二是考慮哪種更高效。
(萬事都是如此啊。)
那具體來看這兩個 classes 的 API 和它們的時間複雜度:
稍微解釋幾個:
add(E e) 是在尾巴上加元素,雖然 ArrayList 可能會有擴容的情況出現,但是均攤複雜度(amortized time complexity)還是 O(1) 的。
add(int index, E e)是在特定的位置上加元素,LinkedList 需要先找到這個位置,再加上這個元素,雖然單純的「加」這個動作是 O(1) 的,但是要找到這個位置還是 O(n) 的。(這個有的人就認為是 O(1),和面試官解釋清楚就行了,拒絕扛精。
remove(int index)是 remove 這個 index 上的元素,是以
- ArrayList 找到這個元素的過程是 O(1),但是 remove 之後,後續元素都要往前移動一位,是以均攤複雜度是 O(n);
- LinkedList 也是要先找到這個 index,這個過程是 O(n) 的,是以整體也是 O(n)。
remove(E e)是 remove 見到的第一個這個元素,那麼
- ArrayList 要先找到這個元素,這個過程是 O(n),然後移除後還要往前移一位,這個更是 O(n),總的還是 O(n);
- LinkedList 也是要先找,這個過程是 O(n),然後移走,這個過程是 O(1),總的是 O(n).
那造成時間複雜度的差別的原因是什麼呢?
答:
- 因為 ArrayList 是用數組來實作的。
- 而數組和連結清單的最大差別就是數組是可以随機通路的(random access)
這個特點造成了在數組裡可以通過下标用 O(1) 的時間拿到任何位置的數,而連結清單則做不到,隻能從頭開始逐個周遊。
也就是說在「改查」這兩個功能上,因為數組能夠随機通路,是以 ArrayList 的效率高。
那「增删」呢?
如果不考慮找到這個元素的時間,
數組因為實體上的連續性,當要增删元素時,在尾部還好,但是其他地方就會導緻後續元素都要移動,是以效率較低;而連結清單則可以輕松的斷開和下一個元素的連接配接,直接插入新元素或者移除舊元素。
但是呢,實際上你不能不考慮找到元素的時間啊。。。而且如果是在尾部操作,資料量大時 ArrayList 會更快的。
是以說:
1.改查選擇 ArrayList;
2.增删在尾部的選擇 ArrayList;
3.其他情況下,如果時間複雜度一樣,推薦選擇 ArrayList,因為 overhead 更小,或者說記憶體使用更有效率。
Vector
那作為 List 的最後一個知識點,我們來聊一下 Vector。這也是一個年齡暴露帖,用過的都是大佬。
那 Vector 和 ArrayList 一樣,也是繼承自 java.util.AbstractList,底層也是用數組來實作的。
但是現在已經被棄用了,因為…它加了太多的 synchronized!
任何好處都是有代價的,線程安全的成本就是效率低,在某些系統裡很容易成為瓶頸,是以現在大家不再在資料結構的層面加 synchronized,而是把這個任務轉移給我們程式員==
那麼面試常問題:Vector 和 ArrayList 的差別是什麼,隻答出來這個還還不太全面。
來看 stack overflow 上的高票回答:
一是剛才已經說過的線程安全問題;
二是擴容時擴多少的差別。
這個得看看源碼:
這是 ArrayList 的擴容實作,這個算術右移操作是把這個數的二進制往右移動一位,最左邊補符号位,但是因為容量沒有負數,是以還是補 0.
那右移一位的效果就是除以 2,那麼定義的新容量就是原容量的 1.5 倍。
不了解這個右移操作符的小夥伴,公衆号内回複「二進制」快複習一下吧~
再來看 Vector 的:
因為通常 capacityIncrement 我們并不定義,是以預設情況下它是擴容兩倍。