導讀:Map居然不屬於Java集合框架的子集?隊列也和List同樣屬於集合的三大子集之一?更有隊列的正確使用姿式,一塊兒來看吧!html
Java中的集合一般指的是Collection下的三個集合框架List、Set、Queue和Map集合,Map並不屬於Collection的子集,而是和它平行的頂級接口。Collection下的子集的關係如文章開頭圖片所示。java
本文的重點將會圍繞: 集合的使用、性能、線程安全、差別性、源碼解讀等幾個方面進行介紹。編程
本文涉及的知識點,分爲兩部分:數組
第一部分,Collection全部子集:安全
第二部分,Map => Hashtable、HashMap、TreeMap、ConcurrentHashMap。數據結構
咱們先來看List、Vector、ArrayList、LinkedList,它們之間的繼承關係圖,以下圖:併發
能夠看出Vector、ArrayList、LinkedList,這三者都是實現集合框架中的List,也就是所謂的有序集合,所以具體功能也比較近似,好比都提供按照位置進行定位、添加或者刪除的操做,都提供迭代器以遍歷其內容等。但由於具體的設計區別,在行爲、性能、線程安全等方面,表現又有很大不一樣。oracle
來看它們的主要方法,以下圖:app
經常使用方法:框架
Vector是Java早期提供的 線程安全的動態數組, 若是不須要線程安全,並不建議選擇,畢竟同步是有額外開銷的。Vector 內部是使用對象數組來保存數據,能夠根據須要自動的增長容量,當數組已滿時,會建立新的數組,並拷貝原有數組數據。
看源代碼能夠知道,咱們Vector是經過 synchronized 實現線程安全的:
public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; }
Vector動態增長容量,源碼查看:
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); }
capacityIncrement變量是what?答案以下:
public Vector(int initialCapacity, int capacityIncrement) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; this.capacityIncrement = capacityIncrement; }
Vector動態增長容量總結: 由上面的源碼可知,若是初始化Vector的時候指定了動態容量擴展大小,就增長指定的動態大小,若是未指定,則擴展一倍的容量。
ArrayList 是應用更加普遍的動態數組,它自己不是線程安全的,因此性能要好不少。
ArrayList的使用與Vector相似,但有着不一樣的動態擴容機制,以下源碼:
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
其中「>> 1」是位運算至關於除2,全部ArrayList擴容是動態擴展50%.
LinkedList 顧名思義是 Java 提供的雙向鏈表,因此它不須要像上面兩種那樣調整容量,它也不是線程安全的,它包含一個很是重要的內部類:Entry。Entry是雙向鏈表節點所對應的數據結構,它包括的屬性有:當前節點所包含的值,上一個節點,下一個節點。
Vector、ArrayList、LinkedList的區別,能夠從如下幾個維度進行對比:
Vector、ArrayList 內部使用數組進行實現,LinkedList 內部使用雙向鏈表實現。
ArrayList 對元素 非末位 的增長和刪除都會引發內存分配空間的動態變化,所以非末位的操做速度較慢,但檢索速度很快。
LinkedList 基於鏈表方式存放數據,增長和刪除元素的速度較快,可是檢索速度較慢。
Vector 使用了synchronized 修飾了操做方法是線程安全的,而 ArrayList、LinkedList 是非線程安全的。
若是須要使用線程安全的List可使用CopyOnWriteArrayList類。
Hashtable、HashMap、TreeMap 都是最多見的一些 Map 實現,是以鍵值對的形式存儲和操做數據的容器類型。
它們之間的關係,以下圖:
HashMap 的性能表現很是依賴於哈希碼的有效性,請務必掌握 hashCode 和 equals 的一些基本約定,好比:
線程安全: Hashtable是線程安全的,HashMap和TreeMap是非線程安全的。HashMap可使用ConcurrentHashMap來保證線程安全。
Set有兩個比較經常使用的子集:HashSet、TreeSet.
HashSet內部使用的是HashMap實現的,看源代碼可知:
public HashSet() { map = new HashMap<>(); }
HashSet也並非線程安全的,HashSet用於存儲無序(存入和取出的順序不必定相同)元素,值也不能重複。
HashSet能夠去除重複的值,以下代碼:
public static void main(String[] args) { Set set = new HashSet(); set.add("orange"); set.add("apple"); set.add("banana"); set.add("grape"); set.add("banana"); System.out.println(set); }
編譯器不會報錯,執行的結果爲:[orange, banana, apple, grape],去掉了重複的「banana」選項。但排序是無序的,若是要實現有序的存儲就要使用TreeSet了。
public static void main(String[] args) { Set set = new TreeSet(); set.add("orange"); set.add("apple"); set.add("banana"); set.add("grape"); set.add("banana"); System.out.println(set); }
輸出的結果是:[apple, banana, grape, orange]
一樣,咱們查看源碼發現,TreeSet的底層實現是TreeMap,源碼以下:
public TreeSet() { this(new TreeMap<E,Object>()); }
TreeSet也是非線程安全的。
Queue(隊列)與棧是相對的一種數據結構。只容許在一端進行插入操做,而在另外一端進行刪除操做的線性表。棧的特色是後進先出,而隊列的特色是先進先出。隊列的用處很大,但大多都是在其餘的數據結構中,好比,樹的按層遍歷,圖的廣度優先搜索等都須要使用隊列作爲輔助數據結構。
Queue的直接子集,以下圖:
其中最經常使用的就是線程安全類:BlockingQueue.
注意:
Queue<String> queue = new LinkedList<String>(); queue.offer("a"); queue.offer("b"); queue.offer("c"); queue.offer("d"); System.out.println(queue); queue.poll(); System.out.println(queue); queue.poll(); queue.poll(); queue.poll(); System.out.println(queue.peek()); // System.out.println(queue.element()); // element 查詢失敗會拋出異常 System.out.println(queue);
ArrayBlockingQueue 底層是數組,有界隊列,若是咱們要使用生產者-消費者模式,這是很是好的選擇。
LinkedBlockingQueue 底層是鏈表,能夠當作無界和有界隊列來使用,因此你們不要覺得它就是無界隊列。
SynchronousQueue 自己不帶有空間來存儲任何元素,使用上能夠選擇公平模式和非公平模式。
PriorityBlockingQueue 是無界隊列,基於數組,數據結構爲二叉堆,數組第一個也是樹的根節點老是最小值。
ArrayBlockingQueue :一個由數組結構組成的有界阻塞隊列。
LinkedBlockingQueue :一個由鏈表結構組成的有界阻塞隊列。
PriorityBlockingQueue :一個支持優先級排序的無界阻塞隊列。
DelayQueue:一個使用優先級隊列實現的無界阻塞隊列。
SynchronousQueue:一個不存儲元素的阻塞隊列。
LinkedTransferQueue:一個由鏈表結構組成的無界阻塞隊列。
LinkedBlockingDeque:一個由鏈表結構組成的雙向阻塞隊列
關於String、StringBuffer、StringBuilder的線程安全
String是典型的Immutable(不可變)類,被聲明爲final全部屬性也都是final,全部它是不可變的,全部拼加、截取等動做等會產生新的String對象。
StringBuffer是爲了解決上面的問題,而誕生的,提供了append方法實現了對字符串的拼加,append方法使用了synchronized實現了線程安全。
StringBuilder是JDK 1.5 新出的特性,做爲StringBuffer的性能補充,StringBuffer的append方法使用了synchronized實現了線程的安全,但同時也帶來了性能開銷,在沒有線程安全的狀況下能夠優先使用StringBuilder。
List 也就是咱們前面介紹最多的有序集合,它提供了方便的訪問、插入、刪除等操做。
Set 是不容許重複元素的,這是和 List 最明顯的區別,也就是不存在兩個對象 equals 返回 true。咱們在平常開發中有不少須要保證元素惟一性的場合。
Queue/Deque 則是 Java 提供的標準隊列結構的實現,除了集合的基本功能,它還支持相似先入先出(FIFO, First-in-First-Out)或者後入先出(LIFO,Last-In-First-Out)等特定行爲。這裏不包括 BlockingQueue,由於一般是併發編程場合,因此被放置在併發包裏。
Map 是廣義 Java 集合框架中的另一部分,Map 接口存儲一組鍵值對象,提供key(鍵)到value(值)的映射。
《碼出高效:Java開發手冊》
Java核心技術36講:http://t.cn/EwUJvWA
Oracle docs:https://docs.oracle.com/javase/tutorial/collections/interfaces/queue.html