前言
宣告,本文用的是jdk1.8
花了一個星期,把Java容器核心的知識過了一遍,感覺集合已經無所畏懼了!!(哈哈哈....),現在來總結一下吧~~
回顧目錄:
- Collection總覽
- List集合就這麼簡單【原始碼剖析】
- Map集合、雜湊表、紅黑樹介紹
- HashMap就是這麼簡單【原始碼剖析】
- LinkedHashMap就這麼簡單【原始碼剖析】
- TreeMap就這麼簡單【原始碼剖析】
- ConcurrentHashMap基於JDK1.8原始碼剖析
- Set集合就這麼簡單!
Java容器可分為兩大類:
- Collection
- List
- ArrayList
- LinkedList
- Vector(瞭解,已過時)
- Set
- HashSet
- LinkedHashSet
- TreeSet
- HashSet
- List
- Map
- HashMap
- LinkedHashMap
- TreeMap
- ConcurrentHashMap
- Hashtable(瞭解,,已過時)
- HashMap
著重標出的那些就是我們用得最多的容器。
其實,我也不知道要怎麼總結好,因為之前寫每一篇的時候都總結過了。現在又把他們重新羅列出來好像有點水,所以,我決定去回答一些Java容器的面試題!
當然了,我的答案未必就是正確的。如果有錯誤的地方大家多多包含,希望不吝在評論區留言指正~~
一、ArrayList和Vector的區別
共同點:
- 這兩個類都實現了List介面,它們都是有序的集合(儲存有序),底層是陣列。我們可以按位置索引號取出某個元素,允許元素重複和為null。
區別:
- 同步性:
- ArrayList是非同步的
- Vector是同步的
- 即便需要同步的時候,我們可以使用Collections工具類來構建出同步的ArrayList而不用Vector
- 擴容大小:
- Vector增長原來的一倍,ArrayList增長原來的0.5倍
二、HashMap和Hashtable的區別
共同點:
- 從儲存結構和實現來講基本上都是相同的,都是實現Map介面~
區別:
- 同步性:
- HashMap是非同步的
- Hashtable是同步的
- 需要同步的時候,我們往往不使用,而使用ConcurrentHashMapConcurrentHashMap基於JDK1.8原始碼剖析
- 是否允許為null:
- HashMap允許為null
- Hashtable不允許為null
- contains方法
- 這知識點是在牛客網刷到的,沒想到這種題還會有(我不太喜歡)....
- Hashtable有contains方法
- HashMap把Hashtable的contains方法去掉了,改成了containsValue和containsKey
- 繼承不同:
- HashMap<K,V> extends AbstractMap<K,V>
- public class Hashtable<K,V> extends Dictionary<K,V>
三、List和Map的區別
共同點:
- 都是Java常用的容器,都是介面(ps:寫出來感覺好像和沒寫一樣.....)
不同點:
- 儲存結構不同:
- List是儲存單列的集合
- Map儲存的是key-value鍵值對的集合
- 元素是否可重複:
- List允許元素重複
- Map不允許key重複
- 是否有序:
- List集合是有序的(儲存有序)
- Map集合是無序的(儲存無序)
四、Set裡的元素是不能重複的,那麼用什麼方法來區分重複與否呢? 是用==還是equals()?
我們知道Set集合實際大都使用的是Map集合的put方法來新增元素。
以HashSet為例,HashSet裡的元素不能重複,在原始碼(HashMap)是這樣體現的:
// 1. 如果key 相等
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
// 2. 修改對應的value
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
複製程式碼
新增元素的時候,如果key(也對應的Set集合的元素)相等,那麼則修改value值。而在Set集合中,value值僅僅是一個Object物件罷了(該物件對Set本身而言是無用的)。
也就是說:Set集合如果新增的元素相同時,是根本沒有插入的(僅修改了一個無用的value值)!從原始碼(HashMap)中也看出來,==和equals()方法都有使用!
五、Collection和Collections的區別
- Collection是集合的上級介面,繼承它的有Set和List介面
- Collections是集合的工具類,提供了一系列的靜態方法對集合的搜尋、查詢、同步等操作
六、說出ArrayList,LinkedList的儲存效能和特性
ArrayList的底層是陣列,LinkedList的底層是雙向連結串列。
- ArrayList它支援以角標位置進行索引出對應的元素(隨機訪問),而LinkedList則需要遍歷整個連結串列來獲取對應的元素。因此一般來說ArrayList的訪問速度是要比LinkedList要快的
- ArrayList由於是陣列,對於刪除和修改而言消耗是比較大(複製和移動陣列實現),LinkedList是雙向連結串列刪除和修改只需要修改對應的指標即可,消耗是很小的。因此一般來說LinkedList的增刪速度是要比ArrayList要快的
6.1擴充套件:
ArrayList的增刪未必就是比LinkedList要慢。
- 如果增刪都是在末尾來操作【每次呼叫的都是remove()和add()】,此時ArrayList就不需要移動和複製陣列來進行操作了。如果資料量有百萬級的時,速度是會比LinkedList要快的。(我測試過)
- 如果刪除操作的位置是在中間。由於LinkedList的消耗主要是在遍歷上,ArrayList的消耗主要是在移動和複製上(底層呼叫的是arraycopy()方法,是native方法)。
- LinkedList的遍歷速度是要慢於ArrayList的複製移動速度的
- 如果資料量有百萬級的時,還是ArrayList要快。(我測試過)
七、Enumeration和Iterator介面的區別
這個我在前面的文章中也沒有詳細去講它們,只是大概知道的是:Iterator替代了Enumeration,Enumeration是一箇舊的迭代器了。
與Enumeration相比,Iterator更加安全,因為當一個集合正在被遍歷的時候,它會阻止其它執行緒去修改集合。
- 我們在做練習的時候,迭代時會不會經常出錯,丟擲ConcurrentModificationException異常,說我們在遍歷的時候還在修改元素。
- 這其實就是fail-fast機制~具體可參考博文:blog.csdn.net/panweiwei19…
區別有三點:
- Iterator的方法名比Enumeration更科學
- Iterator有fail-fast機制,比Enumeration更安全
- Iterator能夠刪除元素,Enumeration並不能刪除元素
八、ListIterator有什麼特點
- ListIterator繼承了Iterator介面,它用於遍歷List集合的元素。
- ListIterator可以實現雙向遍歷,新增元素,設定元素
看一下原始碼的方法就知道了:
九、併發集合類是什麼?
Java1.5併發包(java.util.concurrent)包含執行緒安全集合類,允許在迭代時修改集合。
- Utils包下的集合迭代器被設計為fail-fast的,會丟擲ConcurrentModificationException。但java.util.concurrent的並不會,感謝評論區提醒~
- 一部分類為:
- CopyOnWriteArrayList
- ConcurrentHashMap
- CopyOnWriteArraySet
十、Java中HashMap的key值要是為類物件則該類需要滿足什麼條件?
需要同時重寫該類的hashCode()方法和它的equals()方法。
- 從原始碼可以得知,在插入元素的時候是先算出該物件的hashCode。如果hashcode相等話的。那麼表明該物件是儲存在同一個位置上的。
- 如果呼叫equals()方法,兩個key相同,則替換元素
- 如果呼叫equals()方法,兩個key不相同,則說明該hashCode僅僅是碰巧相同,此時是雜湊衝突,將新增的元素放在桶子上
一般來說,我們會認為:只要兩個物件的成員變數的值是相等的,那麼我們就認為這兩個物件是相等的!因為,Object底層比較的是兩個物件的地址,而對我們開發來說這樣的意義並不大~這也就為什麼我們要重寫equals()
方法
重寫了equals()方法,就要重寫hashCode()的方法。因為equals()認定了這兩個物件相同,而同一個物件呼叫hashCode()方法時,是應該返回相同的值的!
十一、與Java集合框架相關的有哪些最好的實踐
- 根據需要確定集合的型別。如果是單列的集合,我們考慮用Collection下的子介面ArrayList和Set。如果是對映,我們就考慮使用Map~
- 確定完我們的集合型別,我們接下來確定使用該集合型別下的哪個子類~我認為可以簡單分成幾個步驟:
- 是否需要同步
- 去找執行緒安全的集合類使用
- 迭代時是否需要有序(插入順序有序)
- 去找Linked雙向列表結構的
- 是否需要排序(自然順序或者手動排序)
- 去找Tree紅黑樹型別的(JDK1.8)
- 是否需要同步
- 估算存放集合的資料量有多大,無論是List還是Map,它們實現動態增長,都是有效能消耗的。在初始集合的時候給出一個合理的容量會減少動態增長時的消耗~
- 使用泛型,避免在執行時出現ClassCastException
- 儘可能使用Collections工具類,或者獲取只讀、同步或空的集合,而非編寫自己的實現。它將會提供程式碼重用性,它有著更好的穩定性和可維護性
十二、ArrayList集合加入1萬條資料,應該怎麼提高效率
ArrayList的預設初始容量為10,要插入大量資料的時候需要不斷擴容,而擴容是非常影響效能的。因此,現在明確了10萬條資料了,我們可以直接在初始化的時候就設定ArrayList的容量!
這樣就可以提高效率了~
十三、總結
2018年4月15日17:14:03,上面找了一些面試題答了一下,感覺不夠過癮呀。很多我覺得比較重要的知識點我都沒有找到對應的面試題(可能我搜尋的能力太水了?)。
將這篇文章作為集合的總結篇,但覺得沒什麼好寫就回答一些面試題去了,找了一會面試題又覺得不夠系統。而這篇總結我又不想複製前面的章節總結到這裡來。於是我決定畫一個腦圖來結束這篇文章!
2018年4月15日19:31:33 畫完啦!!!!!
需要更多腦圖的同學可關注公眾號:Java3y,回覆【腦圖】即可~
涵蓋Java後端所有知識點的開源專案(已有6 K star):github.com/ZhongFuChen…
如果大家想要實時關注我更新的文章以及分享的乾貨的話,微信搜尋Java3y。
PDF文件的內容均為手打,有任何的不懂都可以直接來問我(公眾號有我的聯絡方式)。