校招面試之——Java容器
最近校招季,特把自己面試中遇到的問題整理整理,以鞏固自己的知識。
Java中對於容器有兩大類儲存方式,一種是單元素存放,還有一種就是key-value這種有關聯的雙元素存放了。對於Java中的容器,有下列的結構圖可以參照:
Collection (用來存放獨立元素的序列)
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
├HashSet
└TreeSet
Map (用來存放key-value型的元素對)
├Hashtable
├HashMap
├TreeMap
└WeakHashMap
下面,我們就來分別講講這幾種容器。
List
List是有序的Collection,使用此介面能控制每個元素插入的位置,使用者能夠使用索引來訪問元素。與Set不同的是,List允許有重複的元素在其中。
- ArrayList
ArrayList相當於順式儲存(線性表),當例項化一個ArrayList時,一個數組也被例項化了,預設初始化一個長度為10的陣列。當新增資料的時候會判斷當前容量是否能夠容下新增的物件,一旦發現容量不足,會自動擴容,新的大小為原有容量的1.5倍+1。
ArrayList可以快速隨機訪問,通過呼叫get(i)方法來訪問下標為i的元素。
LindedList
LinkedList相當於鏈式儲存(雙向連結串列),它是通過節點直接彼此連線來實現的。每一個節點都包括前一個節點的引用,後一個節點的引用和節點儲存的值。
當插入或刪除節點時,只需要修改其中保持先後關係的節點的引用即可,所以,操作其中的物件速度比ArrayList要快的多。但是LinkedList不能隨機訪問元素,雖然它有get()方法,但是這個方法是通過遍歷節點來定位的,速度很慢。
Vector
由於Vector和Stack已經很少使用了,我們暫且不討論它們。
ArrayList和LinkedList的區別
- ArrayList實現了基於動態陣列的資料結構,LinkedList實現了基於連結串列的資料結構
- 對於隨機訪問get和set,ArrayList優於LinkedList
- 對於增刪add和remove,LinkedList優於ArrayList
Set
Set是一種不包含重複元素的Collection,它的建構函式有一個約束條件,傳入的Collection引數不能包含重複的元素。
HashSet
HashSet實現了Set介面,由雜湊表支援。它不保證Set的迭代順序,特別是它不保證該順序恆久不變。HashSet底層使用的容器實際上就是HashMap,它以HashMap的key來儲存所有的元素,value使用一個static final的Object物件來標識。
private static final Object PRESENT = new Object();
TreeSet
TreeSet整體上效能沒有HashSet好,但是它可以維持元素的排序狀態。TreeSet底層使用的容器實際上就是TreeMap,它以TreeMap的key來儲存set集合的元素,value都以一個名為PRESENT的Object物件代替(無實際意義)。
Map
Map介面提供key到value的對映,一個Map不能包含相同的key,每個key只能對映一個value。
HashMap
HashMap底層就是一個數組結構,陣列中的每一項又是一個連結串列(其實就是雜湊表的拉鍊法實現)。但是此類不保證對映的順序,特別是不保證該順序恆久不變。但是TreeMap可以維持對映的順序。Hashtable
和HashMap實現差不多,具體區別見下面的Hashtable和HashMap的區別。TreeMap
TreeMap的底層實現是一個紅黑樹結構,這樣可以保證快速檢索節點,TreeMap可以維持對映的順序。下面我們來具體說下TreeMap的底層實現,首先我們需要了解下排序二叉樹:
- 排序二叉樹:要麼是一棵空二叉樹,要麼是具有下列性質的二叉樹:
- 若它的左子樹不為空,則左子樹上所有節點的值均小於根節點的值
- 若它的右子樹不為空,則右子樹上所有節點的值均大於根節點的值
- 它的左右自子樹也分別為排序二叉樹
對於排序二叉樹,它的中序遍歷就可以得到由小到大的有序序列,所以用它就可以實現快速檢索,但是為什麼Java還要多此一舉用紅黑樹呢?
- 排序二叉樹雖然可以快速檢索,但是在最壞情況下:若插入的節點本身就是有序的,要麼由小到大排列,要麼由大到小排列,那麼最後得到的排序二叉樹就變為了連結串列:所有的節點只有左節點或者所有的節點只有右節點。這種情況下,排序二叉樹就變為了普通連結串列,檢索效率會很差。
所以,Java引入了紅黑樹作為TreeMap的底層實現
- 紅黑樹:紅黑樹是一種更高效的檢索二叉樹,它的性質為:
- 所有的節點都為紅色或者黑色
- 根節點永遠是黑色
- 所有的葉節點都是空節點(NULL),並且是黑色
- 每個紅色節點的兩個子節點都是黑色,即從根節點到葉子節點的路徑上不會出現兩個連續的紅色節點。
- 從任一節點到其子樹中每個葉子節點的路徑都包含相同數量的黑色節點
紅黑樹通過上面的限制來保證它大致是平衡的(因為紅黑樹的高度不會無限增高),這樣保證了紅黑樹在最壞情況下都是高效的,不會出現普通排序二叉樹的情況。
- 排序二叉樹:要麼是一棵空二叉樹,要麼是具有下列性質的二叉樹:
Hashtable和HashMap的區別
繼承和實現不同
Hashtable是繼承自陳舊的Dictionary類,實現了Map介面;HashMap實現介面(繼承自AbstractMap,AbstractMap實現Map介面)執行緒安全不同
Hashtable是執行緒安全的,它的實現方法裡面都添加了synchronized關鍵字來確保執行緒同步。HashMap是執行緒不安全的,在多執行緒程式設計下如使用HashMap需要使用Collections.synchronizedMap()來獲取一個執行緒安全的集合。對null的處理不同
HashMap支援null作為key和value,但是Hashtable不允許(key,value都不允許)。HashMap的方法get()返回null時,既可以表示沒有改鍵,也可以表示該鍵對應的值為null,所以不能用此判斷是否有該鍵,而應該用containsKey()。HashMap初始容量為16,Hashtable初始容量為11。HashMap擴容時是當前容量翻倍:capacity*2,Hashtable是當前容量翻倍+1:capacity*2+1。
雜湊值的使用不同
Hashtable直接使用key的hashcode對table陣列的長度取模,HashMap是對key的hashcode進行二次hash,然後對table陣列的長度取模,以獲得更好的雜湊值。