集合類存放於 Java.util 包中, 主要有 3 種: set(集)、 list(列表包含 Queue)和 map(映射)。java
Collection: Collection 是集合 List、 Set、 Queue 的最基本的接口。算法
Iterator:迭代器,能夠經過迭代器遍歷集合中的數據。數組
Map:是映射表的基礎接口。安全
元素有序,可重複。數據結構
擴容新建數組,複製原數組中元素,開銷很大。多線程
刪除元素將後面元素向前移一位,複雜度On。併發
for循環遍歷數據較快。高併發
//遍歷方式 public class ArrayListTraversal { public void arrayListTraversal(List<Integer> lists){ /* 第一種遍歷方式 */ System.out.print("for循環的遍歷方式:"); for (int i = 0; i < lists.size(); i++) { System.out.print(lists.get(i)); } System.out.println(); /* 第二種遍歷方式 */ System.out.print("foreach的遍歷方式:"); for (Integer list : lists) { System.out.print(list); } System.out.println(); /* 第三種遍歷方式 */ System.out.print("Iterator的遍歷方式:"); for (Iterator<Integer> list = lists.iterator(); list.hasNext();) { System.out.print(list.next()); } } public static void main(String[] args) { List<Integer> lists = new ArrayList<Integer>(); /* 添加元素 */ for (int i = 0; i < 10; i++) { lists.add(i); } new ArrayListTraversal().arrayListTraversal(lists); } }
基於雙向鏈表實現,使用 Node 存儲鏈表節點信息。線程
在列表中插入和刪除速度快,可是查找須要遍歷整個鏈表。設計
foreach遍歷數據較快。
(1) 不管ArrayList仍是LinkedList,遍歷建議使用foreach,尤爲是數據量較大時LinkedList避免使用get遍歷。 (2) List使用首選ArrayList。對於個別插入刪除很是多的可使用LinkedList。 (3) 可能在遍歷List循環內部須要使用到下標,這時綜合考慮下是使用foreach和自增count仍是get方式。
元素無序,不可重複。
對象的相等性本質是對象 hashCode 值判斷(java 是依據對象的內存地址計算出的此序號)。
若是想要讓兩個不一樣的對象視爲相等,必須覆蓋 Object 的 hashCode 方法和 equals 方法。
基於哈希表實現,支持快速查找,但不支持有序性操做,經過HashMap實現。
TreeSet()是使用二叉樹的原理對新 add()的對象按照指定的順序排序(升序、降序),每增長一個對象都會進行排序,將對象插入的二叉樹指定的位置。
繼承 HashSet、基於 LinkedHashMap 實現。
底層使用 LinkedHashMap 來保存全部元素,全部方法操做與 HashSet 相同。
提供了四個構造方法,並經過傳遞一個標識參數,調用父類的構造器,底層構造一個 LinkedHashMap 來實現,在相關操做上與父類 HashSet 的操做相同,直接調用父類 HashSet 的方法便可。
基於哈希表實現。
鍵能夠是null,並且鍵值不能夠重複,若是重複了之後就會對第一個進行鍵值進行覆蓋。
大方向上, HashMap 裏面是一個數組,而後數組中每一個元素是一個單向鏈表。上圖中,每一個綠色的實體是嵌套類 Entry 的實例, Entry 包含四個屬性: key, value, hash 值和用於單向鏈表的 next。 1. capacity:當前數組容量,始終保持 2^n,能夠擴容,擴容後數組大小爲當前的 2 倍。 2. loadFactor:負載因子,默認爲 0.75。 3. threshold:擴容的閾值,等於 capacity * loadFactor。 Java8 對 HashMap 進行了一些修改, 最大的不一樣就是利用了紅黑樹,因此其由 數組+鏈表+紅黑樹 組成。 根據 Java7 HashMap 的介紹,咱們知道,查找的時候,根據 hash 值咱們可以快速定位到數組的具體下標,可是以後的話, 須要順着鏈表一個個比較下去才能找到咱們須要的,時間複雜度取決於鏈表的長度,爲 O(n)。爲了下降這部分的開銷,在 Java8 中, 當鏈表中的元素超過了 8 個之後,會將鏈表轉換爲紅黑樹,在這些位置進行查找的時候能夠下降時間複雜度爲 O(logN)。
HashMap特性總結 1. HashMap的底層是個Node數組(Node<K,V>[] table),在數組的具體索引位置,若是存在多個節點,則多是以鏈表或紅黑樹的形式存在。 2. 增長、刪除、查找鍵值對時,定位到哈希桶數組的位置是很關鍵的一步,源碼中是經過下面3個操做來完成這一步:1)拿到key的hashCode值; 2)將hashCode的高位參與運算,從新計算hash值; 3)將計算出來的hash值與(table.length - 1)進行&運算。 3. HashMap的默認初始容量(capacity)是16,capacity必須爲2的冪次方;默認負載因子(load factor)是0.75;實際能存放的節點個數(threshold,即觸發擴容的閾值)= capacity * load factor。 4. HashMap在觸發擴容後,閾值會變爲原來的2倍,而且會進行重hash,重hash後索引位置index的節點的新分佈位置最多隻有兩個:原索引位置或原索引+oldCap位置。例如capacity爲16,索引位置5的節點擴容後,只可能分佈在新報索引位置5和索引位置21(5+16)。 5. 致使HashMap擴容後,同一個索引位置的節點重hash最多分佈在兩個位置的根本緣由是:1)table的長度始終爲2的n次方;2)索引位置的計算方法爲「(table.length - 1) & hash」。HashMap擴容是一個比較耗時的操做,定義HashMap時儘可能給個接近的初始容量值。 6. HashMap有threshold屬性和loadFactor屬性,可是沒有capacity屬性。初始化時,若是傳了初始化容量值,該值是存在threshold變量,而且Node數組是在第一次put時纔會進行初始化,初始化時會將此時的threshold值做爲新表的capacity值,而後用capacity和loadFactor計算新表的真正threshold值。 7. 當同一個索引位置的節點在增長後達到9個時,會觸發鏈表節點(Node)轉紅黑樹節點(TreeNode,間接繼承Node),轉成紅黑樹節點後,其實鏈表的結構還存在,經過next屬性維持。鏈表節點轉紅黑樹節點的具體方法爲源碼中的treeifyBin(Node<K,V>[] tab, int hash)方法。 8. 當同一個索引位置的節點在移除後達到6個時,而且該索引位置的節點爲紅黑樹節點,會觸發紅黑樹節點轉鏈表節點。紅黑樹節點轉鏈表節點的具體方法爲源碼中的untreeify(HashMap<K,V> map)方法。 9. HashMap在JDK1.8以後再也不有死循環的問題,JDK1.8以前存在死循環的根本緣由是在擴容後同一索引位置的節點順序會反掉。 10. HashMap是非線程安全的,在併發場景下使用ConcurrentHashMap來代替。
//遍歷map import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class TestMap { public static void main(String[] args) { Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "a"); map.put(2, "b"); map.put(3, "ab"); map.put(4, "ab"); map.put(4, "ab");// 和上面相同 , 會本身篩選 System.out.println(map.size()); // 第一種: /* * Set<Integer> set = map.keySet(); //獲得全部key的集合 * * for (Integer in : set) { String str = map.get(in); * System.out.println(in + " " + str); } */ System.out.println("第一種:經過Map.keySet遍歷key和value:"); for (Integer in : map.keySet()) { //map.keySet()返回的是全部key的值 String str = map.get(in);//獲得每一個key多對用value的值 System.out.println(in + " " + str); } // 第二種: System.out.println("第二種:經過Map.entrySet使用iterator遍歷key和value:"); Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<Integer, String> entry = it.next(); System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); } // 第三種:推薦,尤爲是容量大時 System.out.println("第三種:經過Map.entrySet遍歷key和value"); for (Map.Entry<Integer, String> entry : map.entrySet()) { //Map.entry<Integer,String> 映射項(鍵-值對) 有幾個方法:用上面的名字entry //entry.getKey() ;entry.getValue(); entry.setValue(); //map.entrySet() 返回此映射中包含的映射關係的 Set視圖。 System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); } // 第四種: System.out.println("第四種:經過Map.values()遍歷全部的value,但不能遍歷key"); for (String v : map.values()) { System.out.println("value= " + v); } } }
TreeMap 實現 SortedMap 接口,可以把它保存的記錄根據鍵排序,默認是按鍵值的升序排序,也能夠指定排序的比較器,當用 Iterator 遍歷 TreeMap 時,獲得的記錄是排過序的。
若是使用排序的映射,建議使用 TreeMap。
在使用 TreeMap 時, key 必須實現 Comparable 接口或者在構造 TreeMap 傳入自定義的Comparator,不然會在運行時拋出 ava.lang.ClassCastException 類型的異常。
LinkedHashMap 是 HashMap 的一個子類,保存了記錄的插入順序,在用 Iterator 遍歷LinkedHashMap 時,先獲得的記錄確定是先插入的,也能夠在構造時帶參數,按照訪問次序排序。
public interface Queue<E> extends Collection<E> { boolean add(E var1); boolean offer(E var1); E remove(); E poll(); E element(); E peek(); }
Queue<Integer> q=new LinkedList<Integer>();
默認升序,底層爲堆,初始容量11
傳入對象時須要指定比較器
Queue<Integer> q=new PriorityQueue<Integer>(); Queue<Student> q=new PriorityQueue<Student>((e1,e2)->(e1.id-e2.id));
阻塞隊列,在java.util.concurrent包下。
能夠簡單地理解爲經過synchronized來實現同步的容器。
實現與 ArrayList 相似,使用 synchronized 進行同步,訪問速度慢。
線程安全,底層數組,初始10,默認擴容2倍。
棧,繼承Vector類,底層實現是數組,先進後出。
Hashtable 是遺留類,不少映射的經常使用功能與 HashMap 相似。
這些容器實現線程安全的方式就是將它們的狀態封裝起來,並在須要同步的方法上加上關鍵字synchronized。 削弱了併發性,當多個線程共同競爭容器級的鎖時,吞吐量就會下降。
採用CAS算法,部分代碼使用synchronized鎖保證線程安全**。java.util.concurrent包中提供了多種併發類容器。
對應的非併發容器:ArrayList。
目標:代替Vector、synchronizedList。
原理:利用高併發每每是讀多寫少的特性,對讀操做不加鎖,對寫操做,先複製一份新的集合,在新的集合上面修改,而後將新集合賦值給舊的引用,並經過volatile 保證可見性,固然寫操做的鎖是必不可少的。
對應的非併發容器:HashSet。
目標:代替synchronizedSet。
原理:基於CopyOnWriteArrayList實現,其惟一的不一樣是在add時調用 CopyOnWriteArrayList 的addIfAbsent方法,其遍歷當前Object數組,若是Object數組中已有當前元素,則直接返回,若是沒有則放入Object數組的尾部並返回。
對應的非併發容器:HashMap。
目標:代替Hashtable、synchronizedMap,支持複合操做。
原理:JDK6採用更加細粒度的加鎖機制Segment「分段鎖」,JDK8採用CAS無鎖算法。
對應的非併發容器:TreeMap。
目標:代替synchronizedSortedMap(TreeMap)。
原理:Skip list(跳錶)是一種能夠代替平衡樹的數據結構,默認按Key值升序。
對應的非併發容器:TreeSet。
目標:代替synchronizedSortedSet。
原理:內部基於ConcurrentSkipListMap實現。
不會阻塞的隊列。
對應的非併發容器:Queue。
原理:基於鏈表實現的FIFO隊列(LinkedList的併發版本)。
對應的非併發容器:BlockingQueue。
特色:拓展了Queue,增長了可阻塞的插入和獲取等操做。
原理:經過ReentrantLock實現線程安全,經過Condition實現阻塞和喚醒。
實現類: LinkedBlockingQueue:基於鏈表實現的可阻塞的FIFO隊列 ArrayBlockingQueue:基於數組實現的可阻塞的FIFO隊列 PriorityBlockingQueue:按優先級排序的隊列
併發容器是專門針對多線程併發設計的,使用了鎖分段技術,只對操做的位置進行同步操做,可是其餘沒有操做的位置其餘線程仍然能夠訪問,提升了程序的吞吐量。