面試官:小夥子,你連Java集合都講不清楚,怎麼就敢開口要8K呀?

開始之前,先給大家講個小故事吧:
圖片來源於網絡

不是這個~

面試官:你好!請簡單介紹一下你自己

騷年:大佬您好!我在讀書的時候就十分仰慕您,您一直都是我的偶像,所以我職高剛畢業就迫不及待的學Java技術,然後來您所在的公司應聘,沒想到面試官就是您

面試官:等等,職…職高?

騷年:這都不重要,噢~我親愛的大佬,您知道嗎?我非常敬仰您,也羨慕您,羨慕您頭頂一毛不拔的那塊地,那是我一直嚮往的地方…

面試官(摸了摸從右邊蓋到左邊的頭髮):咳咳…,咱迴歸正題,額~這個,剛找工作?

騷年:不完全是,學習期間實習過一段時間

面試官:嗯,先問問,對薪資有什麼期望嗎?

騷年:額~我來算算,我一個月房租1200,一天抽兩包6塊錢的煙,中飯加晚飯一天20,還要喝飲料,用電、用水啥的,算下來8000塊,這樣,我就不賺錢了,就8K!

面試官:就8K?我們這邊最低都是28K,那行,瞭解一下你的技術水平吧,問:前段時間微博服務器崩潰,是怎麼回事?

騷年:啊?

面試官:額~拿錯了臺本,再來,說說Java集合!

騷年:這個我知道,List和Set

面試官:還有呢?

騷年:還有嗎?沒有了吧?

面試官:小夥子,你Java集合都說不全,怎麼敢要8K啊?回家吧!

騷年:…

細說Java集合

點擊此處,獲取免費領取Java核心知識點高清完整大圖、PDF資料、技術視頻

接口繼承關係和實現

集合類存放於 Java.util 包中,主要有 3 種:set(集)、list(列表包含 Queue)和 map(映射)。

1. Collection:Collection 是集合 List、Set、Queue 的最基本的接口

2. Iterator:迭代器,可以通過迭代器遍歷集合中的數據

3. Map:是映射表的基礎接口


List

Java 的 List 是非常常用的數據類型。List 是有序的 Collection。Java List 一共三個實現類:分別是 ArrayList、Vector 和 LinkedList。

1、ArrayList(數組)

ArrayList 是最常用的 List 實現類,內部是通過數組實現的,它允許對元素進行快速隨機訪問。數組的缺點是每個元素之間不能有間隔,當數組大小不滿足時需要增加存儲能力,就要將已經有數組的數據複製到新的存儲空間中。當從 ArrayList 的中間位置插入或者刪除元素時,需要對數組進行復制、移動、代價比較高。因此,它適合隨機查找和遍歷,不適合插入和刪除。

2、Vector(數組實現、線程同步)

Vector 與 ArrayList 一樣,也是通過數組實現的,不同的是它支持線程的同步,即某一時刻只有一個線程能夠寫 Vector,避免多線程同時寫而引起的不一致性,但實現同步需要很高的花費,因此,訪問它比訪問 ArrayList 慢。

3、LinkList(鏈表)

LinkedList 是用鏈表結構存儲數據的,很適合數據的動態插入和刪除,隨機訪問和遍歷速度比較慢。另外,他還提供了 List 接口中沒有定義的方法,專門用於操作表頭和表尾元素,可以當作堆棧、隊列和雙向隊列使用。

Set

Set 注重獨一無二的性質,該體系集合用於存儲無序(存入和取出的順序不一定相同)元素,值不能重複。對象的相等性本質是對象 hashCode 值(java 是依據對象的內存地址計算出的此序號)判斷的,如果想要讓兩個不同的對象視爲相等的,就必須覆蓋 Object 的 hashCode 方法和 equals 方法。

1、HashSet(Hash 表)

哈希表邊存放的是哈希值。HashSet 存儲元素的順序並不是按照存入時的順序(和 List 顯然不同) 而是按照哈希值來存的所以取數據也是按照哈希值取得。元素的哈希值是通過元素的hashcode 方法來獲取的, HashSet 首先判斷兩個元素的哈希值,如果哈希值一樣,接着會比較equals 方法 如果 equls 結果爲 true ,HashSet 就視爲同一個元素。如果 equals 爲 false 就不是同一個元素。

哈希值相同 equals 爲 false 的元素是怎麼存儲呢,就是在同樣的哈希值下順延(可以認爲哈希值相同的元素放在一個哈希桶中)。也就是哈希一樣的存一列。如圖 1 表示 hashCode 值不相同的情況;圖 2 表示 hashCode 值相同,但 equals 不相同的情況。

HashSet 通過 hashCode 值來確定元素在內存中的位置。一個 hashCode 位置上可以存放多個元素。

2、TreeSet(二叉樹)

  1. TreeSet()是使用二叉樹的原理對新 add()的對象按照指定的順序排序(升序、降序),每增加一個對象都會進行排序,將對象插入的二叉樹指定的位置。

  2. Integer 和 String 對象都可以進行默認的 TreeSet 排序,而自定義類的對象是不可以的,自己定義的類必須實現 Comparable 接口,並且覆寫相應的 compareTo()函數,纔可以正常使用。

  3. 在覆寫 compare()函數時,要返回相應的值才能使 TreeSet 按照一定的規則來排序。

  4. 比較此對象與指定對象的順序。如果該對象小於、等於或大於指定對象,則分別返回負整數、零或正整數。

3、LinkHashSet(HashSet+LinkedHashMap)

對於 LinkedHashSet 而言,它繼承與 HashSet、又基於 LinkedHashMap 來實現的。LinkedHashSet 底層使用 LinkedHashMap 來保存所有元素,它繼承與 HashSet,其所有的方法操作上又與 HashSet 相同,因此 LinkedHashSet 的實現上非常簡單,只提供了四個構造方法,並通過傳遞一個標識參數,調用父類的構造器,底層構造一個 LinkedHashMap 來實現,在相關操作上與父類 HashSet 的操作相同,直接調用父類 HashSet 的方法即可。

Map

1、HashMap(數組+鏈表+紅黑樹)

HashMap 根據鍵的 hashCode 值存儲數據,大多數情況下可以直接定位到它的值,因而具有很快的訪問速度,但遍歷順序卻是不確定的。HashMap 最多隻允許一條記錄的鍵爲 null,允許多條記錄的值爲 null。HashMap 非線程安全,即任一時刻可以有多個線程同時寫 HashMap,可能會導致數據的不一致。如果需要滿足線程安全,可以用 Collections 的 synchronizedMap 方法使HashMap 具有線程安全的能力,或者使用 ConcurrentHashMap。我們用下面這張圖來介紹HashMap 的結構。

(1)JAVA7 實現

大方向上,HashMap 裏面是一個數組,然後數組中每個元素是一個單向鏈表。上圖中,每個綠色的實體是嵌套類 Entry 的實例,Entry 包含四個屬性:key, value, hash 值和用於單向鏈表的 next。

  1. capacity:當前數組容量,始終保持 2^n,可以擴容,擴容後數組大小爲當前的 2 倍。

  2. loadFactor:負載因子,默認爲 0.75。

  3. threshold:擴容的閾值,等於 capacity * loadFactor。

(2)JAVA8 實現

Java8 對 HashMap 進行了一些修改,最大的不同就是利用了紅黑樹,所以其由數組+鏈表+紅黑樹組成。

根據 Java7 HashMap 的介紹,我們知道,查找的時候,根據 hash 值我們能夠快速定位到數組的具體下標,但是之後的話,需要順着鏈表一個個比較下去才能找到我們需要的,時間複雜度取決於鏈表的長度,爲 O(n)。爲了降低這部分的開銷,在 Java8 中,當鏈表中的元素超過了 8 個以後,會將鏈表轉換爲紅黑樹,在這些位置進行查找的時候可以降低時間複雜度爲O(logN)。

2、ConcurrentHashMap

(1)Segment 段

ConcurrentHashMap 和 HashMap 思路是差不多的,但是因爲它支持併發操作,所以要複雜一些。整個 ConcurrentHashMap 由一個個 Segment 組成,Segment 代表”部分“或”一段“的意思,所以很多地方都會將其描述爲分段鎖。注意,行文中,我很多地方用了“槽”來代表一個segment。

(2)線程安全(Segment 繼承 ReentrantLock 加鎖)

簡單理解就是,ConcurrentHashMap 是一個 Segment 數組,Segment 通過繼承ReentrantLock 來進行加鎖,所以每次需要加鎖的操作鎖住的是一個 segment,這樣只要保證每個 Segment 是線程安全的,也就實現了全局的線程安全。

(3)並行度(默認 16)

concurrencyLevel:並行級別、併發數、Segment 數,怎麼翻譯不重要,理解它。默認是16,也就是說 ConcurrentHashMap 有 16 個 Segments,所以理論上,這個時候,最多可以同時支持 16 個線程併發寫,只要它們的操作分別分佈在不同的 Segment 上。這個值可以在初始化的時候設置爲其他值,但是一旦初始化以後,它是不可以擴容的。再具體到每個 Segment 內部,其實每個 Segment 很像之前介紹的 HashMap,不過它要保證線程安全,所以處理起來要麻煩些。

(4)Java8 實現 (引入了紅黑樹)

Java8 對 ConcurrentHashMap 進行了比較大的改動,Java8 也引入了紅黑樹。

3、HashTable(線程安全)

Hashtable 是遺留類,很多映射的常用功能與 HashMap 類似,不同的是它承自Dictionary類,並且是線程安全的,任一時間只有一個線程能寫 Hashtable,併發性不如ConcurrentHashMap,因爲ConcurrentHashMap 引入了分段鎖。Hashtable 不建議在新代碼中使用,不需要線程安全的場合可以用 HashMap 替換,需要線程安全的場合可以用ConcurrentHashMap 替換。

4、TreeMap(可排序)

TreeMap 實現 SortedMap 接口,能夠把它保存的記錄根據鍵排序,默認是按鍵值的升序排序,也可以指定排序的比較器,當用 Iterator 遍歷 TreeMap 時,得到的記錄是排過序的。如果使用排序的映射,建議使用 TreeMap。在使用 TreeMap 時,key 必須實現 Comparable 接口或者在構造 TreeMap 傳入自定義的Comparator,否則會在運行時拋出java.lang.ClassCastException 類型的異常。

5、LinkHashMap(記錄插入順序)

LinkedHashMap 是 HashMap 的一個子類,保存了記錄的插入順序,在用 Iterator 編LinkedHashMap 時,先得到的記錄肯定是先插入的,也可以在構造時帶參數,按照訪問次序排序。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章