- 每一個集合類底層采用的數據結構不同,例如ArrayList集合底層采用了數組,LinkedList集合底層采用了雙向鏈表,HashMap集合底層采用了哈希表,TreeMap集合底層采用了紅黑樹。
- **集合中存儲的是引用。**即。集合中存放的是對象的地址,而不是把堆中的實例對象存儲。
- 默認情況下,如果不使用泛型的話,集合中可以存儲任何類型的引用,只要是 Object 的子類都可以存儲。
- Java集合框架相關的類都在 java.util 包下。
- Java 集合框架有兩大支線:
- Collection,可進一步細分為
- List:可重復的集合。具體實現有:
ArrayList(動態數組)
和LinkedList(雙向鏈表)
; - Set:不可重復的集合,典型代表就是
HashSet(哈希表)
和TreeSet(紅黑樹)
; - Queue:隊列。具體實現有:雙端隊列
ArrayDeque
,以及優先級隊列PriorityQueue
。
- List:可重復的集合。具體實現有:
- Map:鍵值對的集合。具體實現有:
HashMap
、LinkedHashMap(雙向鏈表 + 哈希表)
- Collection,可進一步細分為
- 這些具體實現中,只有 TreeSet、LinkedHashMap 是有序的(
鍵有序
)
Collection
1、List
List 的特點是存取有序,可以存放重復的元素,可以用下標對元素進行操作。
1.1 ArrayList
- ArrayList 是由數組實現的,支持隨機存取,也就是可以通過下標直接存取元素;
- 從尾部插入和刪除元素會比較快捷,從中間插入和刪除元素會比較低效,這是由數組的特點決定的;
- 如果內部數組的容量不足時會自動擴容,因此當元素非常龐大的時候,效率會比較低。
1.2 LinkedList
- LinkedList 是由雙向鏈表實現的,不支持隨機存取,只能從一端開始遍歷,直到找到需要的元素后返回;
- 任意位置插入和刪除元素都很方便,因為只需要改變前一個節點和后一個節點的引用即可,不像 ArrayList 那樣需要復制和移動數組元素,這是由鏈表的特點決定的;
- 因為每個元素都存儲了前一個和后一個節點的引用,所以相對來說,占用的內存空間會比 ArrayList 多一些。
// CRUD 實例
// 創建一個集合
LinkedList<String> list = new LinkedList<String>();
// 添加元素
list.add("apple");
list.add("avocado");// 遍歷集合 for 循環
for (int i = 0; i < list.size(); i++) {String s = list.get(i);System.out.println(s);
}
// 遍歷集合 for each
for (String s : list) {System.out.println(s);
}// 刪除元素
list.remove(1);
// 遍歷集合
for (String s : list) {System.out.println(s);
}// 修改元素
list.set(1, "apricot");
1.3 ArrayList 和 LinkedList
1.4 Vector 和 Stack
List 的實現類還有一個 Vector,是一個元老級的類,比 ArrayList 出現得更早。
ArrayList 和 Vector 非常相似,只不過 Vector 是線程安全的,像 get、set、add 這些方法都加了 synchronized 關鍵字,就導致執行效率會比較低,所以現在已經很少用了。
這種加了同步方法的類,注定會被淘汰掉,就像StringBuilder 取代 StringBuffer那樣。
Stack 是 Vector 的一個子類,本質上也是由動態數組實現的,只不過還實現了先進后出的功能(在 get、set、add 方法的基礎上追加了 pop「返回并移除棧頂的元素」、peek「只返回棧頂元素」等方法),所以叫棧。
由于 Stack 執行效率比較低(方法上同樣加了 synchronized 關鍵字),就被雙端隊列 ArrayDeque 取代了(下面會介紹)。
2、Set
3、Queue
3.1 ArrayDeque
ArrayDeque 是一個基于數組實現的雙端隊列,為了滿足可以同時在數組兩端插入或刪除元素的需求,數組必須是循環的,也就是說數組的任何一點都可以被看作是起點或者終點。
3.2 LinkedList
LinkedList 一般應該歸在 List 下,只不過,它也實現了 Deque 接口,可以作為隊列來使用。
實際上,LinkedList 同時實現了 Stack、Queue、PriorityQueue 的所有功能。
// 演示 LinkedList 的隊列特性
LinkedList<String> queue = new LinkedList<>();// 區別于 add, offer元素用于向隊尾添加元素
queue.offer("apple");
queue.offer("avocado");
System.out.println(queue); // 輸出 [apple, avocado]// 刪除元素
queue.poll();
System.out.println(queue); // 輸出 [avocado]// 修改元素:LinkedList 中的元素不支持直接修改,需要先刪除再添加
String first = queue.poll();
queue.offer("apple");
System.out.println(queue); // 輸出 [apple]// 查找元素:既可以查找下標,也可以查找內容
System.out.println(queue.get(0)); // 輸出 apple
System.out.println(queue.contains("apricot")); // 輸出 false// 查找元素:使用迭代器的方式查找陳清揚
// 使用迭代器依次遍歷元素并查找
Iterator<String> iterator = queue.iterator();
while (iterator.hasNext()) {String element = iterator.next();if (element.equals("apple")) {System.out.println("找到了:" + element);break;}
}
3.2.1 add–remove / offer–poll
LinkedList 同時實現了 List 和 Deque(雙端隊列)接口,因此,其同時擁有兩組插入/刪除的方法:
- add–remove:List 接口中定義的方法
- 在 LinkedList 實例的尾部進行插入和刪除
- offer–poll:Deque 接口中定義的方法
- 在 LinkedList 實例的尾部進行插入,頭部進行刪除(即隊列的特征)
3.3 ArrayDeque 和 LinkedList
LinkedList 和 ArrayDeque 都是 Java 集合框架中的雙向隊列(deque),它們都支持在隊列的兩端進行元素的插入和刪除操作。
不過,LinkedList 和 ArrayDeque 在實現上有一些不同:
- 底層實現方式不同:LinkedList 是基于鏈表實現的,而 ArrayDeque 是基于數組實現的。
- 隨機訪問的效率不同:由于底層實現方式的不同,LinkedList 對于隨機訪問的效率較低,時間復雜度為 O(n),而 ArrayDeque 可以通過下標隨機訪問元素,時間復雜度為 O(1)。
- 迭代器的效率不同:LinkedList 對于迭代器的效率比較低,因為需要通過鏈表進行遍歷,時間復雜度為 O(n),而 ArrayDeque 的迭代器效率比較高,因為可以直接訪問數組中的元素,時間復雜度為 O(1)。
- 內存占用不同:由于 LinkedList 是基于鏈表實現的,它在存儲元素時需要額外的空間來存儲鏈表節點,因此內存占用相對較高,而 ArrayDeque 是基于數組實現的,內存占用相對較低。
- 如何選擇?
- 如果需要在雙向隊列的兩端進行頻繁的插入和刪除操作,并且需要隨機訪問元素,可以考慮使用 ArrayDeque;
- 如果需要在隊列中間進行頻繁的插入和刪除操作,可以考慮使用 LinkedList。‘
3.4 PriorityQueue
PriorityQueue 是一種優先級隊列,它的出隊順序與元素的優先級有關,執行 remove 或者 poll 方法,返回的總是優先級最高的元素。
Map
- Map 保存的是鍵值對,鍵要求保持唯一性,值可以重復。
- Map 接口的常見實現類有兩個:
- HashMap
- TreeMap
1. HashMap
- HashMap 可以根據
鍵
快速地查找對應的值——通過哈希函數將鍵映射到哈希表中的一個索引位置,從而實現快速訪問。 - HashMap 中的鍵和值都可以為 null。如果鍵為 null,則將該鍵映射到哈希表的第一個位置。
- 可以使用迭代器或者 forEach 方法遍歷 HashMap 中的鍵值對。
- HashMap 有一個初始容量和一個負載因子。初始容量是指哈希表的初始大小,負載因子是指哈希表在擴容之前可以存儲的鍵值對數量與哈希表大小的比率。默認的初始容量是 16,負載因子是 0.75。
// HashMap 實例
HashMap<String, String> hashMap = new HashMap<>();// 添加鍵值對
hashMap.put("蘋果", "apple");
hashMap.put("牛油果", "avocado");
hashMap.put("香蕉","Banana");// 遍歷 HashMap
for (String key : hashMap.keySet()) {String value = hashMap.get(key);System.out.println(key + " 對應的值為:" + value);
}
// HashMap 中,鍵的順序是根據鍵的哈希碼計算結果來決定的,因此,不保證有序
// 輸出為 蘋果-- 香蕉--牛油果// 獲取指定鍵的值
String value1 = hashMap.get("蘋果");
System.out.println("蘋果對應的值為:" + value1);// 修改鍵對應的值
hashMap.put("蘋果", "apple1");
String value2 = hashMap.get("蘋果");
System.out.println("修改后蘋果對應的值為:" + value2);// 刪除指定鍵的鍵值對
hashMap.remove("牛油果");
2. LinkedHashMap
- LinkedHashMap 是 HashMap 的子類,。也就是說,只要是 LinkedHashMap ,那么必然也是一個 HashMap。
- LinkedHashMap 可以看作是 HashMap + LinkedList 的合體。它使用哈希表來存儲數據;使用雙向鏈表來記錄插入/訪問元素的順序,維持順序。
- 這里的順序,指的是維持插入的順序。
// LinkedHashMap 實例
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("蘋果", "apple");
linkedHashMap.put("牛油果", "avocado");
linkedHashMap.put("香蕉","Banana");// 遍歷 LinkedHashMap
for (String key : linkedHashMap.keySet()) {String value = linkedHashMap.get(key);System.out.println(key + " 對應的值為:" + value);
}
// linkedHashMap 保證順序,輸出為 蘋果--牛油果--香蕉,可對比 HashMap 的無序
3. TreeMap
- TreeMap 實現了 SortedMap 接口,可以自動將鍵按照自然順序或指定的比較器順序排序,并保證其元素的順序。
- 內部使用紅黑樹來實現鍵的排序和查找。
- 這里的順序,指的是以鍵值為依據,按照某種規則排序。
// TreeMap 實例
Map<String, String> treeMap = new TreeMap<>();// 向 TreeMap 中添加鍵值對
treeMap.put("c", "cat");
treeMap.put("a", "apple");
treeMap.put("b", "banana");// 遍歷 TreeMap
for (Map.Entry<String, String> entry : treeMap.entrySet()) {System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 按照默認順序對鍵進行了排序,輸出是 a--b--c// 查找鍵值對
String name = "c";
if (treeMap.containsKey(name)) {System.out.println("找到了 " + name + ": " + treeMap.get(name));// 輸出 c:cat
} // 修改鍵值對
name = "a";
if (treeMap.containsKey(name)) {System.out.println("修改前的 " + name + ": " + treeMap.get(name));// 輸出 a:appletreeMap.put(name, "aovcado");System.out.println("修改后的 " + name + ": " + treeMap.get(name));// 輸出 a:aovcado
} // 刪除鍵值對
name = "b";
if (treeMap.containsKey(name)) {System.out.println("刪除前的 " + name + ": " + treeMap.get(name));treeMap.remove(name);System.out.println("刪除后的 " + name + ": " + treeMap.get(name));// 輸出 b:null
}
ArrayList
- ArrayList 和 HashMap,算是最常用的兩個集合框架實現。
- ArrayList 基于數組實現了 List 接口。
- Java 原生數組的大小是固定的,但 ArrayList 提供了自動擴容。