Java容器類是java提供的工具包,包含了常用的數據結構:集合、鏈表、隊列、棧、數組、映射等。從本文開始將開啟一個系列詳細分析Java容器中的每個成員,包括源代碼分析,性能分析,不同容器之間對比等等,鏈接將同步更新在本文和置頂博文內。
Java容器主要可以划分為4個部分:List列表、Set集合、Map映射、工具類(Iterator迭代器、Enumeration枚舉類、Arrays和Collections)
容器類框架
Java容器工具包框架圖:
通過上圖,可以把握兩個基本主體,即Collection和Map。
-
Collection是一個接口,是高度抽象出來的集合,它包含了集合的基本操作和屬性。Collection包含了List和Set兩大分支。
List是一個有序的隊列,每一個元素都有它的索引。第一個元素的索引值是0。List的實現類有LinkedList, ArrayList, Vector, Stack。
Set是一個不允許有重復元素的集合。 Set的實現類有HastSet和TreeSet。HashSet依賴於HashMap,它實際上是通過HashMap實現的;TreeSet依賴於TreeMap,它實際上是通過TreeMap實現的。 -
Map是一個映射接口,即key-value鍵值對。Map中的每一個元素包含“一個key”和“key對應的value”。
AbstractMap是個抽象類,它實現了Map接口中的大部分API。而HashMap,TreeMap,WeakHashMap都是繼承於AbstractMap。
Hashtable雖然繼承於Dictionary,但它實現了Map接口。 -
Iterator是遍歷集合的工具,即我們通常通過Iterator迭代器來遍歷集合。我們說Collection依賴於Iterator,是因為Collection的實現類都要實現iterator()函數,返回一個Iterator對象。ListIterator是專門為遍歷List而存在的。
-
Enumeration是JDK 1.0引入的抽象類。作用和Iterator一樣,也是遍歷集合;但是Enumeration的功能要比Iterator少。在上面的框圖中,Enumeration只能在Hashtable, Vector, Stack中使用。
-
Arrays和Collections是操作數組、集合的兩個工具類。
有了上面的整體框架之后,我們接下來對每個類分別進行分析。
Collection接口
Collection的定義如下:
public interface Collection<E> extends Iterable<E> {}
它是一個接口,是高度抽象出來的集合,它包含了集合的基本操作:添加、刪除、清空、遍歷(讀取)、是否為空、獲取大小、是否保護某元素等等。
在Java中所有實現了Collection接口的類都必須提供兩套標准的構造函數,一個是無參,用於創建一個空的Collection,一個是帶有Collection參數的有參構造函數,用於創建一個新的Collection,這個新的Collection與傳入進來的Collection具備相同的元素。
Collection的API:
abstract boolean add(E object) abstract boolean addAll(Collection<? extends E> collection) abstract void clear() abstract boolean contains(Object object) abstract boolean containsAll(Collection<?> collection) abstract boolean equals(Object object) abstract int hashCode() abstract boolean isEmpty() abstract Iterator<E> iterator() abstract boolean remove(Object object) abstract boolean removeAll(Collection<?> collection) abstract boolean retainAll(Collection<?> collection) abstract int size() abstract <T> T[] toArray(T[] array) abstract Object[] toArray()
List接口
List的定義如下:
public interface List<E> extends Collection<E> {}
List是一個繼承於Collection的接口,即List是集合中的一種。List是有序的隊列,List中的每一個元素都有一個索引;第一個元素的索引值是0,往后的元素的索引值依次+1。和Set不同,List中允許有重復的元素。
官方文檔:A List is a collection which maintains an ordering for its elements. Every element in the List has an index. Each element can thus be accessed by its index, with the first index being zero. Normally, Lists allow duplicate elements, as compared to Sets, where elements have to be unique.
關於API方面。既然List是繼承於Collection接口,它自然就包含了Collection中的全部函數接口;由於List是有序隊列,它也額外的有自己的API接口。主要有“添加、刪除、獲取、修改指定位置的元素”、“獲取List中的子隊列”等。
// Collection的API abstract boolean add(E object) abstract boolean addAll(Collection<? extends E> collection) abstract void clear() abstract boolean contains(Object object) abstract boolean containsAll(Collection<?> collection) abstract boolean equals(Object object) abstract int hashCode() abstract boolean isEmpty() abstract Iterator<E> iterator() abstract boolean remove(Object object) abstract boolean removeAll(Collection<?> collection) abstract boolean retainAll(Collection<?> collection) abstract int size() abstract <T> T[] toArray(T[] array) abstract Object[] toArray() // 相比與Collection,List新增的API: abstract void add(int location, E object) abstract boolean addAll(int location, Collection<? extends E> collection) abstract E get(int location) abstract int indexOf(Object object) abstract int lastIndexOf(Object object) abstract ListIterator<E> listIterator(int location) abstract ListIterator<E> listIterator() abstract E remove(int location) abstract E set(int location, E object) abstract List<E> subList(int start, int end)
實現List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。
ArrayList
ArrayList定義如下:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList是一個動態數組,也是我們最常用的集合。它允許任何符合規則的元素插入甚至包括null。每一個ArrayList都有一個初始容量:
private static final int DEFAULT_CAPACITY = 10;
隨着容器中的元素不斷增加,容器的大小也會隨着增加。在每次向容器中增加元素的同時都會進行容量檢查,當快溢出時,就會進行擴容操作。所以如果我們明確所插入元素的多少,最好指定一個初始容量值,避免過多的進行擴容操作而浪費時間、效率。
size、isEmpty、get、set、iterator 和 listIterator 操作都以固定時間運行。add 操作以分攤的固定時間運行,也就是說,添加 n 個元素需要 O(n) 時間(由於要考慮到擴容,所以這不只是添加元素會帶來分攤固定時間開銷那樣簡單)。
ArrayList擅長於隨機訪問。同時ArrayList是非同步的。
LinkedList
LinkedList定義如下:
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
同樣實現List接口的LinkedList與ArrayList不同,ArrayList是一個動態數組,而LinkedList是一個雙向鏈表。所以它除了有ArrayList的基本操作方法外還額外提供了get,remove,insert方法在LinkedList的首部或尾部。
由於實現的方式不同,LinkedList不能隨機訪問,它所有的操作都是要按照雙重鏈表的需要執行。在列表中索引的操作將從開頭或結尾遍歷列表(從靠近指定索引的一端,節約一半時間)。這樣做的好處就是可以通過較低的代價在List中進行插入和刪除操作。
與ArrayList一樣,LinkedList也是非同步的。如果多個線程同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在創建List時構造一個同步的List:
List list = Collections.synchronizedList(new LinkedList(…));
Vector
Vector定義如下:
public class Vector<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
與ArrayList相似,但是Vector是同步的。所以說Vector是線程安全的動態數組。它的操作與ArrayList幾乎一樣。
Stack
Stack定義如下:
public class Stack<E> extends Vector<E> {}
Stack繼承自Vector,實現一個后進先出的堆棧。Stack提供5個額外的方法使得Vector得以被當作堆棧使用。基本的push和pop方法,還有peek方法得到棧頂的元素,empty方法測試堆棧是否為空,search方法檢測一個元素在堆棧中的位置。Stack剛創建后是空棧。
Set接口
Set定義如下:
public interface Set<E> extends Collection<E> {}
Set是一個繼承於Collection的接口,Set是一種不包括重復元素的Collection。它維持它自己的內部排序,所以隨機訪問沒有任何意義。與List一樣,它同樣運行null的存在但是僅有一個。由於Set接口的特殊性,所有傳入Set集合中的元素都必須不同,
關於API方面。Set的API和Collection完全一樣。
實現了Set接口的集合有:HashSet、TreeSet、LinkedHashSet、EnumSet。
HashSet
HashSet定義如下:
public class HashSet<E> extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
HashSet堪稱查詢速度最快的集合,因為其內部是以HashCode來實現的。集合元素可以是null,但只能放入一個null。它內部元素的順序是由哈希碼來決定的,所以它不保證set的迭代順序;特別是它不保證該順序恆久不變。
TreeSet
TreeSet定義如下:
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
TreeSet是二叉樹實現的,基於TreeMap,生成一個總是處於排序狀態的set,內部以TreeMap來實現,不允許放入null值。它是使用元素的自然順序對元素進行排序,或者根據創建Set時提供的 Comparator 進行排序,具體取決於使用的構造方法。
LinkedHashSet
LinkedHashSet定義如下:
public class LinkedHashSet<E> extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable
LinkedHashSet集合同樣是根據元素的hashCode值來決定元素的存儲位置,但是它同時使用鏈表維護元素的次序。這樣使得元素看起 來像是以插入順序保存的,也就是說,當遍歷該集合時候,LinkedHashSet將會以元素的添加順序訪問集合的元素。LinkedHashSet在迭代訪問Set中的全部元素時,性能比HashSet好,但是插入時性能稍微遜色於HashSet。
EnumSet
EnumSet定義如下:
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable
EnumSet中所有值都必須是指定枚舉類型的值,它的元素也是有序的,以枚舉值在枚舉類的定義順序來決定集合元素的順序。EnumSet集合不允許加入null元素,否則會拋出NullPointerException異常。EnumSet類沒有暴露任何構造器來創建該類的實例,程序應該通過它提供的static方法來創建EnumSet對象,例如:
public static void main(String[] args) { //創建一個EnumSet空集合,指定其集合元素是season1的枚舉值 EnumSet<MyEnum>eSet1 = EnumSet.noneOf(MyEnum.class); ////創建一個EnumSet集合,集合元素就是Season里的全部枚舉值 EnumSet<MyEnum>eSet2 = EnumSet.allOf(MyEnum.class); } enum MyEnum { BLACK, WHITE, RED, BLUR, GREEN, YELLOW }
來看一個例子感受一下存儲元素的區別:
static Collection fill(Collection<String> collection) { collection.add("rat"); collection.add("cat"); collection.add("dog"); collection.add("dog"); return collection; } public static void main(String[] args) { System.out.println(fill(new HashSet<String>())); System.out.println(fill(new TreeSet<>())); System.out.println(fill(new LinkedHashSet<>())); }
HashSet是哈希表實現的,存儲順序並無意義。如果存儲順序很重要,那么可以使用TreeSet,它按比較結果的升序保存對象;或者使用LinkedHashSet,它按照被添加的順序保存元素。當然它們都是線程不安全的。
//Result: [rat, cat, dog] [cat, dog, rat] [rat, cat, dog]
Map接口
Map與List、Set接口不同,它是由一系列鍵值對組成的集合,提供了key到Value的映射。在Map中它保證了key與value之間的一一對應關系。也就是說一個key對應一個value,所以它不能存在相同的key值,當然value值可以相同。
實現map的集合有:HashMap、HashTable、TreeMap、WeakHashMap。
HashMap
HashMap定義如下:
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
以哈希表數據結構實現,查找對象時通過哈希函數計算其位置,它是為快速查詢而設計的,其內部定義了一個hash表數組(Entry[] table),元素會通過哈希轉換函數將元素的哈希地址轉換成數組中存放的索引,如果有沖突,則使用散列鏈表的形式將所有相同哈希地址的元素串起來,可能通過查看HashMap.Entry的源碼它是一個單鏈表結構。
HashTable
HashTable的定義如下:
public class Hashtable<K,V> extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable
也是以哈希表數據結構實現的,解決沖突時與HashMap也一樣也是采用了散列鏈表的形式。HashTable繼承Dictionary類,實現Map接口。其中Dictionary類是任何可將鍵映射到相應值的類(如 Hashtable)的抽象父類。每個鍵和每個值都是一個對象。在任何一個 Dictionary 對象中,每個鍵至多與一個值相關聯。Map是”key-value鍵值對”接口。 HashTable采用”拉鏈法”實現哈希表不過性能比HashMap要低。
TreeMap
TreeMap的定義如下:
public class TreeMap<K,V> extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
有序散列表,實現SortedMap接口,底層通過紅黑樹實現。
WeakHashMap
WeakHashMap的定義如下:
public class WeakHashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>
談WeakHashMap前先看一下Java中的引用(強度依次遞減)
- 強引用:普遍對象聲明的引用,存在便不會GC
- 軟引用:有用但並非必須,發生內存溢出前,二次回收
- 弱引用:只能生存到下次GC之前,無論是否內存足夠
- 虛引用:唯一目的是在這個對象被GC時能收到一個系統通知
以弱鍵實現的基於哈希表的Map。在 WeakHashMap 中,當某個鍵不再正常使用時,將自動移除其條目。更精確地說,對於一個給定的鍵,其映射的存在並不阻止垃圾回收器對該鍵的丟棄,這就使該鍵成為可終止的,被終止,然后被回收。丟棄某個鍵時,其條目從映射中有效地移除,因此,該類的行為與其他的 Map 實現有所不同。null值和null鍵都被支持。該類具有與HashMap類相似的性能特征,並具有相同的效能參數初始容量和加載因子。像大多數集合類一樣,該類是不同步的。
Iterator
Iterator定義如下:
public interface Iterator<E> {}
Iterator是一個接口,它是集合的迭代器。集合可以通過Iterator去遍歷集合中的元素。Iterator提供的API接口,包括:是否存在下一個元素、獲取下一個元素、刪除當前元素。
注意:Iterator遍歷Collection時,是fail-fast機制的。即,當某一個線程A通過iterator去遍歷某集合的過程中,若該集合的內容被其他線程所改變了;那么線程A訪問集合時,就會拋出ConcurrentModificationException異常,產生fail-fast事件。關於fail-fast的詳細內容,我們會在后面專門進行說明。
Iterator的API:
abstract boolean hasNext() abstract E next() abstract void remove()
最后用一張圖總結一下大體框架,后面會開始具體分析
鏈接:http://alexyyek.github.io/2015/04/06/Collection/