1. 程式人生 > >java容器類

java容器類

iterator queue接口 set code mage 內存優化 try 根據 array

一、 容器類:

下圖摘自《Java編程思想》,很好地展示了整個容器類的結構。

技術分享

由上圖可知,容器類庫可分為兩大類,各自實現了Collection接口和Map接口,下面就常見的類進行一下分類:

實現Collection接口的容器類

Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
├Set

│├TreeSet
│└HashSet

└LinkedHashSet

├Queue

│├LinkedList

│├DelayQueue
│└PriorityQueue

實現Map接口的容器類

Map
├HashMap

└LinkedHashMap

├Hashtable

├IdentityHashMap

├TreeMap
└WeakHashMap

容器類由兩個頂層接口自上而下擴展:

  • Collection: 存放獨立元素的序列
  • Map:存放key—value類型的鍵值對元素。

  其中值得註意的是:Collection提供了Iterable()模式,可以獲取到Iterator對集合內的元素進行遍歷,而Map則需要先得到Collection進而得到Iterator進行遍歷。

  其中 接口List、Set、Queue實現了Collection接口,對應的List、Set和Queue又有其具有實現的類。實現了Map接口的主要有HashMap、LinkedHashMap、Hashtable、IdentityHashMap、TreeMap、WeakHashMap。

二、 實現Collection接口的容器類

  下面就List、Set和Queue下的幾種常見容器展開簡要的介紹

2.1 List接口

  兩種典型實現:

  • LinkedList:

  底層實現為鏈表,對於這種實現結構而言,插入和刪除操作效率高,而隨機訪問元素時效率較ArrayList低。

  同時,又由於LinkedList實現了Deque接口,故而它還能提供List接口中沒有定義的方法,專門用於操作表頭和表尾的元素,可以當做堆棧和隊列使用。

  • ArrayList:

底層實現為數組,此種結構在隨機訪問和查詢時效率高,而在進行插入和刪除操作時效率較低。此外,由於其底層實現是數組,故而當數組大小不足需要增加存儲空間時,就需要將現有數組中已有的數據復制到新的存儲空間中。

兩種棄用的List實現:

  • Vector:

Vector和ArrayList一樣也是通過數組實現的,故而其特性和ArrayList類似(Vector底層數組實現在擴容時是擴展1倍,而ArrayList則擴展50%+1個),其中值得特別註意的一點是Vector是一種線程安全的容器,即在某一時刻只有一個線程能夠寫Vector,避免在多線程同時寫時引起的不一致性,但由於實現同步(synchronized關鍵字)需要較高的代價,故訪問速度較慢。

  • Stack:

是Vector的一個子類,實現了一個標準的後進先出的棧。(現在實現堆棧的功能一般使用LinkedList)

2.2 Set接口

是一種不包含重復元素的Collection,同時Set允許null元素。加入set的元素必須定義equals()方法來確保對象的唯一性。

幾種典型的實現:

  • Hashset:

利用哈希函數進行了查詢效率上的優化,是一種為快速查找而設計的Set,存入其中的元素必須定義hashCode();

  • LinkedHashSet:

具有Hashset的查詢速度,且內部使用鏈表來維護元素的順序。元素同樣必須定義HashCode()方法

  • TreeSet:

保持有序的Set(實現了SortedSet接口),底層結構為紅黑樹,使用它可以從Set中得到有序的序列。元素必須實現Comparable接口

2.3 Queue接口:

  • PriorityQueue:

存儲在其中的元素該需要實現Comparable接口,即自己完成優先級的定義

  • Deque:

雙向隊列,可以在任何一段添加或移除元素。其中LinkedList實現了Deque接口,可實現隊列或棧的數據結構。

三、實現Map接口的容器類

  Map沒有繼承Collection的接口,其提供了key到value的映射,一個Map中不能包含相同的key,每個key只能映射一個value。

  幾種典型的實現:

  • Hashtable:

  線程安全

  任何非空(non-null)的對象都可作為key或者value。

  添加數據使用put(key, value),取出數據使用get(key),這兩個基本操作的時間開銷為常數。

通過initial capacity和load factor兩個參數調整性能。通常缺省的load factor 0.75較好地實現了時間和空間的均衡。增大load factor可以節省空間但相應的查找時間將增大,這會影響像get和put這樣的操作。

由於key的對象將通過計算其散列函數確定與之對應的value的位置,因此任何作為key的對象必須實現hashcode和equeals方法。

  • HashMap:

線程不安全

HashMap和Hashtable類似,不同之處在於HashMap是非同步的,並且允許null,即null value和null key,但是將HashMap視為Collection時(values()方法可返回Collection),其叠代子操作時間開銷和HashMap的容量成比例。因此,如果叠代操作的性能相當重要的話,不要將HashMap的初始化容量設得過高,或者load factor過低

  • IdentityHashMap:

比較鍵(和值)時使用引用“==”代替equal。即在 IdentityHashMap 中,當且僅當 (k1==k2) 時,才認為兩個鍵 k1 和 k2 相等

而在正常 Map 實現(如 HashMap)中,當且僅當滿足下列條件時才認為兩個鍵 k1 和 k2 相等:(k1==null ? k2==null : e1.equals(e2)))。

該類是應用於特定場景下,即當我們必須使用地址相等來判斷值相等的場合,以及我們確定只要其地址不相等,則其equals方法的結果也必定不相等的場合。

一個很好的例子就是線程本地存儲中的ThreadLocal類,該類的原理是根據Thread從其內部的Map中獲取線程獨立的值,那麽當我們使用只判斷地址相等的IdentityHashMap就會比HashMap要快一些。

  • WeakHashMap:
    WeakHashMap是一種改進的HashMap,它對key實行“弱引用”,如果一個key不再被外部所引用,那麽該key可以被GC回收。
  • ConcurrentHashMap

線程安全的Map,與HashTable不同的,它的線程安全的實現不涉及到同步加鎖,它引入了一個“分段鎖”的概念,即將一個大的Map拆分成多個”HashTable”(實質上是Segment),當多線程訪問容器中不同數據段的數據時,線程間就不存在鎖的競爭關系。

ConcurrentHashMap是由Segment數組和HashEntry數組結構組成。其中Segment是一種可重入鎖,HashEntry則用於存儲鍵值對數據。

Segment的結構和HashMap類似,是一種數組和鏈表的結構。一個Segment中包含一個HashEntry數組。每個HashEntry是一個鏈表結構的元素。每個Segment守護著 一個HashEntry中的元素,當要對HashEntry數組的數據進行修改時,必須首先獲得與它對應的Segment鎖。

三、怎麽選用容器類

  在日常開發中,怎麽選用合適的容器類關乎到程序的性能以及正確性等,所以怎麽選用合適的容器類很關鍵。

  首先一點,容器類中存儲的都是對象的引用,而非對象本身。出於便利的角度,一般簡稱對象的引用為對象。正如上面講到的不同的容器存儲的對象類型不同,並且具有的方法也不同,或是在進行相同操作時的效率也存在差異。

  簡單來講,我們將存儲的對象分為元素和鍵值對兩種。

  元素一般采用實現Collection接口的容器類存儲。又根據元素是否有序(TreeSet),元素是否唯一有所區別(實現Set接口的類),是否要求線程安全(Vector),常進行的操作(ArrayList和LinkedList的選用)等要求進行篩選;

  而鍵值對則采用實現Map接口的容器類進行存儲,又根據是否要求線程安全(ConcurrentHashMap)、是否有必要進行內存的釋放(針對內存優化的WeakHashMap)、是否要求有序(TreeMap)、IdentityHashMap(是否可以用==替換equals來提示效率)等要求來進行篩選。

java容器類