List、Set、Map有什么異同(詳解)


引言:Java集合框架提供了一套性能優良、使用方便的接口和類,它們位於java.util包中

 

 Java集合框架(常用接口):

      Collection 接口存儲一組不唯一,無序的對象(父類接口)

      List 接口存儲一組不唯一,有序(插入順序)的對象

      Set 接口存儲一組唯一,無序的對象

      Map接口存儲一組鍵值對象,提供key到value的映射

一、List接口

           List是一個繼承於Collection的接口,即List是集合中的一種。List是有序的隊列,List中的每一個元素都有一個索引;第一個元素的索引值是0,往后的元素的索引值依次+1。和Set不同,List中允許有重復的元素。實現List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。

       1.可以允許重復的對象。

   2.可以插入多個null元素。

        3.是一個有序容器,保持了每個元素的插入順序,輸出的順序就是插入的順序。

        4.常用的實現類有 ArrayList、LinkedList 和 Vector。ArrayList 最為流行,它提供了使用索引的隨意訪問,而 LinkedList 則對於經常需要從 List 中添加或刪除元素的場合更為合適。

ArrayList

           ArrayList是一個動態數組,也是我們最常用的集合。它允許任何符合規則的元素插入甚至包括null。每一個ArrayList都有一個初始容量:

private static final int DEFAULT_CAPACITY = 10;

隨着容器中的元素不斷增加,容器的大小也會隨着增加。在每次向容器中增加元素的同時都會進行容量檢查,當快溢出時,就會進行擴容操作。所以如果我們明確所插入元素的多少,最好指定一個初始容量值,避免過多的進行擴容操作而浪費時間、效率。

size、isEmpty、get、set、iterator 和 listIterator 操作都以固定時間運行。add 操作以分攤的固定時間運行,也就是說,添加 n 個元素需要 O(n) 時間(由於要考慮到擴容,所以這不只是添加元素會帶來分攤固定時間開銷那樣簡單)。

ArrayList擅長於隨機訪問。同時ArrayList是非同步的。

LinkedList

          同樣實現List接口的LinkedList與ArrayList不同,ArrayList是一個動態數組,而LinkedList是一個雙向鏈表。所以它除了有ArrayList的基本操作方法外還額外提供了get,remove,insert方法在LinkedList的首部或尾部。

由於實現的方式不同,LinkedList不能隨機訪問,它所有的操作都是要按照雙重鏈表的需要執行。在列表中索引的操作將從開頭或結尾遍歷列表(從靠近指定索引的一端,節約一半時間)。這樣做的好處就是可以通過較低的代價在List中進行插入和刪除操作。

與ArrayList一樣,LinkedList也是非同步的。如果多個線程同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在創建List時構造一個同步的List:

List list = Collections.synchronizedList(new LinkedList(…));

Vector

         與ArrayList相似,但是Vector是同步的。所以說Vector是線程安全的動態數組。它的操作與ArrayList幾乎一樣。

Stack

         Stack繼承自Vector,實現一個后進先出的堆棧。Stack提供5個額外的方法使得Vector得以被當作堆棧使用。基本的push和pop方法,還有peek方法得到棧頂的元素,empty方法測試堆棧是否為空,search方法檢測一個元素在堆棧中的位置。Stack剛創建后是空棧。

二、Set接口

           Set是一個繼承於Collection的接口,Set是一種不包括重復元素的Collection。它維持它自己的內部排序,所以隨機訪問沒有任何意義。與List一樣,它同樣運行null的存在但是僅有一個。由於Set接口的特殊性,所有傳入Set集合中的元素都必須不同,關於API方面。Set的API和Collection完全一樣。實現了Set接口的集合有:HashSet、TreeSet、LinkedHashSet、EnumSet。

       1.不允許重復對象

   2. 無序容器,你無法保證每個元素的存儲順序,TreeSet通過 Comparator  或者 Comparable 維護了一個排序順序。

        3. 只允許一個 null 元素

        4.Set 接口最流行的幾個實現類是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基於 HashMap 實現的 HashSet;TreeSet 還實現了 SortedSet 接口,因此 TreeSet 是一個根據其 compare() 和 compareTo() 的定義進行排序的有序容器。

HashSet

         HashSet堪稱查詢速度最快的集合,因為其內部是以HashCode來實現的。集合元素可以是null,但只能放入一個null。它內部元素的順序是由哈希碼來決定的,所以它不保證set的迭代順序;特別是它不保證該順序恆久不變。

TreeSet

         TreeSet是二叉樹實現的,基於TreeMap,生成一個總是處於排序狀態的set,內部以TreeMap來實現,不允許放入null值。它是使用元素的自然順序對元素進行排序,或者根據創建Set時提供的 Comparator 進行排序,具體取決於使用的構造方法。

LinkedHashSet

         LinkedHashSet集合同樣是根據元素的hashCode值來決定元素的存儲位置,但是它同時使用鏈表維護元素的次序。這樣使得元素看起 來像是以插入順序保存的,也就是說,當遍歷該集合時候,LinkedHashSet將會以元素的添加順序訪問集合的元素。LinkedHashSet在迭代訪問Set中的全部元素時,性能比HashSet好,但是插入時性能稍微遜色於HashSet。

三、Map接口

         Map與List、Set接口不同,它是由一系列鍵值對組成的集合,提供了key到Value的映射。在Map中它保證了key與value之間的一一對應關系。也就是說一個key對應一個value,所以它不能存在相同的key值,當然value值可以相同。實現map的集合有:HashMap、HashTable、TreeMap、WeakHashMap。

      1.Map不是collection的子接口或者實現類。Map是一個接口。

       2.Map 的 每個 Entry 都持有兩個對象,也就是一個鍵一個值,Map 可能會持有相同的值對象但鍵對象必須是唯一的。

       3. TreeMap 也通過 Comparator  或者 Comparable 維護了一個排序順序。

       4. Map 里你可以擁有隨意個 null 值但最多只能有一個 null 鍵。

       5.Map 接口最流行的幾個實現類是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)

HashMap

         以哈希表數據結構實現,查找對象時通過哈希函數計算其位置,它是為快速查詢而設計的,其內部定義了一個hash表數組(Entry[] table),元素會通過哈希轉換函數將元素的哈希地址轉換成數組中存放的索引,如果有沖突,則使用散列鏈表的形式將所有相同哈希地址的元素串起來,可能通過查看HashMap.Entry的源碼它是一個單鏈表結構。

HashTable

        也是以哈希表數據結構實現的,解決沖突時與HashMap也一樣也是采用了散列鏈表的形式。HashTable繼承Dictionary類,實現Map接口。其中Dictionary類是任何可將鍵映射到相應值的類(如 Hashtable)的抽象父類。每個鍵和每個值都是一個對象。在任何一個 Dictionary 對象中,每個鍵至多與一個值相關聯。Map是”key-value鍵值對”接口。 HashTable采用”拉鏈法”實現哈希表不過性能比HashMap要低。

TreeMap

        有序散列表,實現SortedMap接口,底層通過紅黑樹實現。

LinkedHashMap

        LinkedHashMap它的特點主要在於linked,帶有這個字眼的就表示底層用的是鏈表來進行的存儲。相對於其他的無序的map實現類,還有像TreeMap這樣的排序類,linkedHashMap最大的特點在於有序,但是它的有序主要體現在先進先出FIFIO上。沒錯,LinkedHashMap主要依靠雙向鏈表和hash表來實現的。

WeakHashMap

談WeakHashMap前先看一下Java中的引用(強度依次遞減)

  1. 強引用:普遍對象聲明的引用,存在便不會GC
  2. 軟引用:有用但並非必須,發生內存溢出前,二次回收
  3. 弱引用:只能生存到下次GC之前,無論是否內存足夠
  4. 虛引用:唯一目的是在這個對象被GC時能收到一個系統通知

以弱鍵實現的基於哈希表的Map。在 WeakHashMap 中,當某個鍵不再正常使用時,將自動移除其條目。更精確地說,對於一個給定的鍵,其映射的存在並不阻止垃圾回收器對該鍵的丟棄,這就使該鍵成為可終止的,被終止,然后被回收。丟棄某個鍵時,其條目從映射中有效地移除,因此,該類的行為與其他的 Map 實現有所不同。null值和null鍵都被支持。該類具有與HashMap類相似的性能特征,並具有相同的效能參數初始容量和加載因子。像大多數集合類一樣,該類是不同步的。

四、總結

1、List、Set都是繼承自Collection接口,Map則不是

2、List特點:元素有放入順序,元素可重復 ,Set特點:元素無放入順序,元素不可重復,重復元素會覆蓋掉,(注意:元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決定的,其位置其實是固定的,加入Set 的Object必須定義equals()方法 ,另外list支持for循環,也就是通過下標來遍歷,也可以用迭代器,但是set只能用迭代,因為他無序,無法用下標來取得想要的值。) 

3、Set和List對比: 

      Set:檢索元素效率低下,刪除和插入效率高,插入和刪除不會引起元素位置改變。 

      List:和數組類似,List可以動態增長,查找元素效率高,插入刪除元素效率低,因為會引起其他元素位置改變。 

4、Map適合儲存鍵值對的數據

5、線程安全集合類與非線程安全集合類 :

  • LinkedList、ArrayList、HashSet是非線程安全的,Vector是線程安全的;
  • HashMap是非線程安全的,HashTable是線程安全的;
  • StringBuilder是非線程安全的,StringBuffer是線程安全的。

五:List、Set、Map的使用場景(面試題)

 

  1. 如果你經常會使用索引來對容器中的元素進行訪問,那么 List 是你的正確的選擇。如果你已經知道索引了的話,那么 List 的實現類比如 ArrayList 可以提供更快速的訪問,如果經常添加刪除元素的,那么肯定要選擇LinkedList。

  2. 如果你想容器中的元素能夠按照它們插入的次序進行有序存儲,那么還是 List,因為 List 是一個有序容器,它按照插入順序進行存儲。

  3. 如果你想保證插入元素的唯一性,也就是你不想有重復值的出現,那么可以選擇一個 Set 的實現類,比如 HashSet、LinkedHashSet 或者 TreeSet。所有 Set 的實現類都遵循了統一約束比如唯一性,而且還提供了額外的特性比如 TreeSet 還是一個 SortedSet,所有存儲於 TreeSet 中的元素可以使用 Java 里的 Comparator 或者 Comparable 進行排序。LinkedHashSet 也按照元素的插入順序對它們進行存儲。

  4. 如果你以鍵和值的形式進行數據存儲那么 Map 是你正確的選擇。你可以根據你的后續需要從 Hashtable、HashMap、TreeMap 中進行選擇。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM