一 Collection接口
1.List
1.1ArrayList
特點
1.底層實現基於動態數組,數組特點根據下表查找元素速度所以查找速度較快.繼承自接口 Collection ->List->ArrayList
2.擴充機制 初始化時數組是空數組,調用add()第一次存放元素時長度默認為10,滿了擴容機制 原來數組 + 原來數組的一半 使用數組copy()方法
2.1 構造一個初始容量為空列表。(不給指定大小時為空)
2.2使用add()方法,將指定的元素追加到列表的末尾。集合的長度 size ++ 先使用后自增1
2.3 將數組長度設置為10
2.4判斷長度是否大於當前數組 調用grow()方法擴容
2.5 newCapacity = oldCapacity + (oldCapacity >> 1); oldCapacity >> 1其實就是oldCapacity 除以2,返回一個copyOf的數組
3.線程不安全 ,效率高
1.2vector
1.采用動態數組,初始化時默認長度10
2.由於是默認初始化 沒有給定capacityIncrement 所以為 0 所以新數組長度為 oldCapacity + oldCapacity 也就是增長為原來2倍,如果給定增長值capacityIncrement
后,擴充為:原來大小+增量也就是newCapacity= oldCapacity + capacityIncrement
1.3LinkedList
1.適合插入,刪除操作,性能高
2.有序的元素,元素可以重復
3.原理
3.1插入時只需讓88元素記住前后倆元素, 刪除88元素只需讓88前面元素(6)記住該元素后面(35)元素 時間復雜度 O(1)< O(logn)< O(n)< O(n^2)
3.2對於某一元素,如何找到它的下一個元素的存放位置呢?對每個數據元素ai,除了存儲其本身的信息之外,
還需存儲一個指示其直接后繼存放位置的指針。在每個結點中再設一個指向前驅的指針域
prior 前面節點指針域 next后一個節點指針域
(圖片來源<<數據結構 java語言描述>>)
3.3 add() 時在尾部追加元素 ...等等
2 set接口
2.1HashSet
1.元素輸出時不允許元素重復,允許元素為null (因為string 重寫了hashCode,equals方法所以可以去重)
2.無序的
3.實現源碼
3.1 使用默認無參初始化時 其實使用的是HashMap實現的
構造一個新的空集;支持的stt>HashMaps/tt>實例具有默認的初始容量(16)和負載因子(0.75)。
3.2 向HashSet添加元素時 使用HashMap的put()方法,把值作為HashMap的key (key肯定不可以重復的,唯一的)
所以詳細實現源碼下面介紹HashMap時再說
4. HashSet的子類LinkedHashSet 底層使用雙向鏈表
4.特點 4.1.按照添加順序輸出 ; 元素不重復
4.2使用無參構造的初始化容量(16)和負載因子(0.75)構造一個新的空鏈接哈希集。
4.3調用父類HashSet 構造 初始化的對象是LinkedHashMap() 關系又跑到map上去了
4.4LinkedHashMap()調用他爹(HashMap)的構造
4.5 調用LinkedHashMap() 構造時 設置了hash順序
accessOrder //這個鏈接哈希映射的迭代方法:true表示訪問順序,false表示插入順序。
2.2TreeSet
1.可以排序(元素按照自然排序進行排列 根據KEY值排序)且不允許元素重復
為什么會自動排序呢?
1.1 默認無參構造時 初始化的是一個TreeMap對象
2.2 調用add()方法時調用的是Map接口提供的put()方法 而treeMap 實現了map接口的put(); 所以底層存儲就是treeMap 紅黑樹
方法 源碼如下

public V put(K key, V value) { Entry<K,V> t = root; if (t == null) { compare(key, key); // type (and possibly null) check root = new Entry<>(key, value, null); size = 1; modCount++; return null; } int cmp; Entry<K,V> parent; // split comparator and comparable paths Comparator<? super K> cpr = comparator; if (cpr != null) { do { parent = t; cmp = cpr.compare(key, t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } else { if (key == null) throw new NullPointerException(); @SuppressWarnings("unchecked") Comparable<? super K> k = (Comparable<? super K>) key; do { parent = t; cmp = k.compareTo(t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } Entry<K,V> e = new Entry<>(key, value, parent); if (cmp < 0) parent.left = e; else parent.right = e; fixAfterInsertion(e); size++; modCount++; return null; }
2.不允許元素為null
3.排序想按照對象的某個值排序,某個類實現接口 Comparator 重寫 compare()方法

private static void treeSet(){ //TreeSet Catss cat1=new Catss(2,"貓1",1); Catss cat2=new Catss(1,"貓2",2); Catss cat3=new Catss(3,"貓3",3); Catss cat4=new Catss(1,"貓4",4); Catss cat5=new Catss(1,"貓4",4); //TreeSet存儲數據要使用比較器 TreeSet<Catss> t=new TreeSet<>(new CatComparator()); t.add(cat1); t.add(cat2); t.add(cat3); t.add(cat4); t.add(cat5); for (Object f:t){ System.out.println(f+"輸出順序是按照年齡排序遞增(年齡有一樣的將會覆蓋一個,只輸出一個)");//輸出順序是按照年齡排序遞增(年齡有一樣的將會覆蓋一個,只輸出一個) } } class CatComparator implements Comparator<Catss> { @Override public int compare(Catss o1, Catss o2) { return o1.getAge()-o2.getAge();//根據年齡排序 } }
二 Map接口
2.1 HashMap
特點:
1.無序的鍵值對存儲一組對象
2.key必須保證唯一,Value可以重復
3.key 和value可以為null
4.線程不安全
5.默認加載因子0.75,默認數組大小16 6.實現基於哈希表 (數組+鏈表/紅黑樹)
源碼:1.加載因子0.75f
2.實現map接口的put()方法 hashMap重寫put
2.1為了一次存取便能得到所查記錄,在記錄的存儲位置和它的關鍵字之間建立一個確定的對應關系H,
以H(key)作為關鍵字為key的記錄在表中的位置,稱這個對應關系H為哈希(Hash)函數。按這個思想建立的表為哈希表。
2.2解決Hash沖突
根據設定的哈希函數H(key)和所選中的處理沖突的方法,將一組關鍵字映射到一個有限的、
地址連續的地址集(區間)上,並以關鍵字在地址集中的“象”作為相應記錄在表中的存儲位置,如此構造所得
的查找表稱之為“哈希表”,這一映射過程也稱為“散列”,所以哈希表也稱散列表。--<<數據結構 java語言描述>>
2.3子類LinkHashMap,按照添加順序輸出特點參考上面LinkedHashSet
put()方法的大致流程如下:
-
首先,將插入的鍵值對通過hash算法計算出對應的桶位置。
-
檢查該桶位置上是否已經存在元素。如果該桶位置上沒有元素,則直接將鍵值對插入到該位置上。
-
如果該桶位置上已經存在元素,則需要進行鏈表或紅黑樹的操作。
-
如果該位置上的元素個數小於8個,則將該元素添加到鏈表中。
-
如果該位置上的元素個數已經達到8個,則將鏈表轉換為紅黑樹。如果該位置上的元素已經是紅黑樹,則直接將該鍵值對插入到紅黑樹中。
-
-
在插入鍵值對后,如果HashMap的大小超過了負載因子(load factor)*當前數組長度,則需要進行擴容操作。
-
在擴容時,會創建一個新的數組,並將原來數組中的元素重新分配到新的數組中。
-
擴容后,新的數組的大小為原來數組的兩倍,並將閾值(threshold)也擴大為原來的兩倍。
-
-
最后,將鍵值對插入到桶位置中,並返回null或原來桶位置上的值。
public V put(K key, V value) { // 計算鍵的hash值 return putVal(hash(key), key, value, false, true); } final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { // 獲取當前HashMap的table數組,如果為null,則進行初始化 Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; // 計算key在數組中的下標i if ((p = tab[i = (n - 1) & hash]) == null) // 如果當前位置為空,則直接創建新節點 tab[i] = newNode(hash, key, value, null); else { // 如果當前位置不為空,判斷當前節點是否為要添加的key Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) // 如果是樹節點,則進行紅黑樹添加操作 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { // 如果當前位置是鏈表,則遍歷鏈表,查找key是否已存在 for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { // 如果鏈表中沒有找到key,則創建新節點添加到鏈表尾部 p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st // 如果鏈表長度已達到閾值,則進行鏈表轉換為紅黑樹的操作 treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } // 如果e不為null,則說明key已存在,將其舊值替換為新值 if (e != null) { V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; // 在LinkedHashMap中使用,記錄節點訪問順序 afterNodeAccess(e); return oldValue; } } ++modCount; // 如果HashMap中鍵值對數量已達到閾值,則進行擴容 if (++size > threshold) resize(); // 在LinkedHashMap中使用,記錄節點插入順序 afterNodeInsertion(evict); return null; }
2.2Hashtable
特點 1.線程安全調用put()時synchronized修飾
2.默認構造大小11擴充因子0.75
3.無序的,K和V 都不能為null ,允許元素重復
2.3TreeMap
特點:1.可以排序(自然排序123),元素可以重復,K值不能重復

public static void treeMapTest(){ TreeMap<String, String> treeMap = new TreeMap<>(); treeMap.put("1","A"); treeMap.put("4","A"); treeMap.put("2","B"); treeMap.put("3","C"); treeMap.forEach((k,v)->System.out.println("K值:"+k+"----->value值:"+v)); } //K值:1----->value值:A //K值:2----->value值:B //K值:3----->value值:C //K值:4----->value值:A
2.排序想按照對象的某個值排序,某個類實現接口 Comparator 重寫 compare()方法; 上面TreeSet 一樣
補充
1.負載因子0.75

除此之外,hash表里還有一個“負載極限”,“負載極限”是一個0~1的數值,“負載極限”決定了hash表的最大填滿程度。當hash表中的負載因子達到指定的“負載極限”時,hash表會自動成倍地增加容量(桶的數量),並將原有的對象重新分配,放入新的桶內,這稱為rehashing。HashSet和HashMap、Hashtable的構造器允許指定一個負載極限,HashSet和HashMap、Hashtable默認的“負載極限”為0.75,這表明當該hash表的3/4已經被填滿時,hash表會發生rehashing。“負載極限”的默認值(0.75)是時間和空間成本上的一種折中:較高的“負載極限”可以降低hash表所占用的內存空間,但會增加查詢數據的時間開銷,而查詢是最頻繁的操作(HashMap的get()與put()方法都要用到查詢);較低的“負載極限”會提高查詢數據的性能,但會增加hash表所占用的內存開銷。程序員可以根據實際情況來調整HashSet和HashMap的“負載極限”值。如果開始就知道HashSet和HashMap、Hashtable會保存很多記錄,則可以在創建時就使用較大的初始化容量,如果初始化容量始終大於HashSet和HashMap、Hashtable所包含的最大記錄數除以負載極限,就不會發生rehashing。使用足夠大的初始化容量創建HashSet和HashMap、Hashtable時,可以更高效地增加記錄,但將初始化容量設置太高可能會浪費空間,因此通常不要將初始化容量設置得太高 --<<java瘋狂講義>>
2.map集合的遍歷

public class Map09 { public static void main(String[] args){ hashMap(); } private static void hashMap(){ Map<Integer,String> map=new HashMap<>();//Map<key,value> map.put(1,"key必須保證唯一"); map.put(2,"無序的"); map.put(3,"put方法"); map.put(null,null); System.out.println(map.size()); System.out.println("得到key=2的值:"+map.get(2)); //第一種遍歷 lambda表達式遍歷forEach();非常簡便 System.out.println("---------lambda表達式遍歷forEach()-----------------------"); map.forEach((i, s) -> System.out.println("key="+i+" value:"+s)); //第二種遍歷 entrySet()方法 System.out.println("---------entrySet()方法使用foreach遍歷-----------------------"); Set<Map.Entry<Integer,String>> entry=map.entrySet(); for (Map.Entry e:entry){ System.out.println(e.getKey()+"->"+e.getValue()); } System.out.println("---------entrySet()方法使用Iterator遍歷-----------------------"); Set entries = map.entrySet(); Iterator iterator = entries.iterator(); while(iterator.hasNext()){ Map.Entry entrys = (Map.Entry) iterator.next(); Object key = entrys.getKey(); String value = map.get(key); System.out.println(key+" "+value+"-----"); } System.out.println("----------keySet()方法---------------------"); //第三種 遍歷鍵 keySet()方法 Set<Integer> key=map.keySet(); for (Integer i:key){ String va=map.get(i); System.out.println(i+"->"+va); } //第四種遍歷值map.values() System.out.println("----------values()方法---------------------"); Collection<String> vs=map.values(); for (String str:vs){ System.out.println(str); } } }