一、集合框架概述
1.1 什么是集合框架
Java集合框架(Java Collections Framework, JCF)是Java語言中用于表示和操作集合的一套標準化體系結構。它提供了一組接口、實現類和算法,用于存儲和操作對象組,解決了數組在存儲對象時的諸多限制。
集合框架的主要優勢包括:
-
減少編程工作量:提供通用的數據結構和算法
-
提高程序性能:經過優化的實現
-
促進API互操作性:統一的接口規范
-
降低學習成本:一致的架構設計
-
提高軟件質量:經過充分測試的代碼
1.2 集合框架的體系結構
Java集合框架主要由以下幾部分組成:
-
接口:定義集合的抽象數據類型
-
實現:接口的具體實現類
-
算法:對集合進行操作的方法,如排序、搜索等
集合框架的核心接口層次結構如下:
Iterable └── Collection├── List├── Set│ └── SortedSet└── Queue└── Deque
此外還有獨立的Map
接口及其子接口SortedMap
。
1.3 集合框架的歷史演變
Java集合框架經歷了幾個重要的發展階段:
-
JDK 1.0:只有Vector、Hashtable等簡單集合類
-
JDK 1.2:引入完整的集合框架
-
JDK 1.5:加入泛型支持
-
JDK 1.6:性能優化和小幅API增強
-
JDK 1.7:引入鉆石操作符等語法糖
-
JDK 1.8:加入Stream API和Lambda表達式支持
-
JDK 9:新增不可變集合工廠方法
-
JDK 10:引入var類型推斷
-
JDK 11+:持續優化和增強
二、核心接口詳解
2.1 Collection接口
Collection
是集合框架的根接口,定義了所有集合共有的基本操作:
java
public interface Collection<E> extends Iterable<E> {// 基本操作int size();boolean isEmpty();boolean contains(Object element);boolean add(E element);boolean remove(Object element);Iterator<E> iterator();// 批量操作boolean containsAll(Collection<?> c);boolean addAll(Collection<? extends E> c);boolean removeAll(Collection<?> c);boolean retainAll(Collection<?> c);void clear();// 數組操作Object[] toArray();<T> T[] toArray(T[] a);// JDK 8新增的默認方法default boolean removeIf(Predicate<? super E> filter) { ... }default Spliterator<E> spliterator() { ... }default Stream<E> stream() { ... }default Stream<E> parallelStream() { ... }
}
2.2 List接口
List
是有序集合(也稱為序列),允許重復元素和null值。主要特點包括:
-
精確控制每個元素的插入位置
-
可以通過索引訪問元素
-
可以搜索元素
-
允許對列表進行迭代
java
public interface List<E> extends Collection<E> {// 位置訪問操作E get(int index);E set(int index, E element);void add(int index, E element);E remove(int index);int indexOf(Object o);int lastIndexOf(Object o);// 列表迭代器ListIterator<E> listIterator();ListIterator<E> listIterator(int index);// 范圍視圖List<E> subList(int fromIndex, int toIndex);// JDK 8新增的默認方法default void replaceAll(UnaryOperator<E> operator) { ... }default void sort(Comparator<? super E> c) { ... }
}
2.3 Set接口
Set
是不包含重復元素的集合,最多包含一個null元素。它模擬了數學上的集合概念,不保證元素的順序(除非使用SortedSet)。
java
public interface Set<E> extends Collection<E> {// 從Collection繼承的方法// JDK 8新增的默認方法default Spliterator<E> spliterator() { ... }
}
2.4 Queue接口
Queue
是一種特殊的集合,用于在處理前保存元素。隊列通常(但不一定)以FIFO(先進先出)方式排序元素。
java
public interface Queue<E> extends Collection<E> {// 插入操作boolean add(E e);boolean offer(E e);// 移除操作E remove();E poll();// 檢查操作E element();E peek();
}
2.5 Map接口
Map
不是真正的集合(不繼承Collection接口),但完全集成在集合框架中。它將鍵映射到值,鍵不能重復,每個鍵最多映射到一個值。
java
public interface Map<K,V> {// 基本操作int size();boolean isEmpty();boolean containsKey(Object key);boolean containsValue(Object value);V get(Object key);V put(K key, V value);V remove(Object key);void putAll(Map<? extends K, ? extends V> m);void clear();// 集合視圖Set<K> keySet();Collection<V> values();Set<Map.Entry<K, V>> entrySet();// 內部Entry接口interface Entry<K,V> {K getKey();V getValue();V setValue(V value);boolean equals(Object o);int hashCode();// JDK 8新增的默認方法public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() { ... }public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() { ... }}// JDK 8新增的默認方法default V getOrDefault(Object key, V defaultValue) { ... }default void forEach(BiConsumer<? super K, ? super V> action) { ... }default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { ... }default V putIfAbsent(K key, V value) { ... }default boolean remove(Object key, Object value) { ... }default boolean replace(K key, V oldValue, V newValue) { ... }default V replace(K key, V value) { ... }default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { ... }default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { ... }default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { ... }default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { ... }
}
三、主要實現類分析
3.1 List實現類
3.1.1 ArrayList
基于動態數組的實現,非線程安全。特點:
-
隨機訪問快(O(1))
-
尾部插入刪除快(O(1))
-
中間插入刪除慢(O(n))
-
內存占用較少
java
// 典型使用場景
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add(1, "C++"); // 在索引1處插入// 遍歷方式
for (String s : list) {System.out.println(s);
}// JDK 8+的forEach方法
list.forEach(System.out::println);
3.1.2 LinkedList
基于雙向鏈表的實現,同時實現了List和Deque接口。特點:
-
任意位置插入刪除快(O(1))
-
隨機訪問慢(O(n))
-
內存占用較多(需要存儲前后節點引用)
java
// 典型使用場景
List<Integer> linkedList = new LinkedList<>();
linkedList.add(10);
linkedList.addFirst(5); // 作為Deque使用
linkedList.addLast(15); // 作為Deque使用// 迭代器遍歷
Iterator<Integer> it = linkedList.iterator();
while (it.hasNext()) {System.out.println(it.next());
}
3.1.3 Vector
線程安全的動態數組實現,方法都使用synchronized修飾。已逐漸被ArrayList和Collections.synchronizedList取代。
java
// 使用示例
Vector<String> vector = new Vector<>();
vector.add("Old");
vector.add("Collection");
3.1.4 CopyOnWriteArrayList
線程安全的List實現,適合讀多寫少的場景。所有修改操作都會創建底層數組的新副本。
java
// 使用示例
CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>();
cowList.add("Thread-safe");
cowList.add("List");
3.2 Set實現類
3.2.1 HashSet
基于HashMap實現的Set,使用對象的hashCode()方法確定元素位置。特點:
-
元素無序
-
添加、刪除、包含操作都是O(1)
-
允許null元素
java
// 使用示例
Set<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Apple"); // 重復元素不會被添加System.out.println(set.contains("Apple")); // true
3.2.2 LinkedHashSet
繼承自HashSet,但同時維護了一個雙向鏈表來保持插入順序。特點:
-
迭代順序可預測
-
性能略低于HashSet
-
適合需要保持插入順序的場景
java
// 使用示例
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("First");
linkedHashSet.add("Second");
linkedHashSet.add("Third");// 遍歷時會按照插入順序輸出
linkedHashSet.forEach(System.out::println);
3.2.3 TreeSet
基于TreeMap實現的NavigableSet,元素按照自然順序或Comparator排序。特點:
-
元素有序
-
添加、刪除、包含操作都是O(log n)
-
不允許null元素(除非Comparator允許)
java
// 使用示例
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(5);
treeSet.add(2);
treeSet.add(8);// 遍歷時會按照自然順序輸出
treeSet.forEach(System.out::println); // 輸出 2,5,8
3.2.4 CopyOnWriteArraySet
基于CopyOnWriteArrayList實現的線程安全Set,適合讀多寫少的場景。
java
// 使用示例
CopyOnWriteArraySet<String> cowSet = new CopyOnWriteArraySet<>();
cowSet.add("Thread-safe");
cowSet.add("Set");
3.2.5 EnumSet
專為枚舉類型設計的高性能Set實現,內部使用位向量表示。
java
// 使用示例
enum Day { MONDAY, TUESDAY, WEDNESDAY }
EnumSet<Day> days = EnumSet.of(Day.MONDAY, Day.WEDNESDAY);
3.3 Queue實現類
3.3.1 LinkedList
如前所述,LinkedList也實現了Deque接口,因此可以作為隊列使用。
java
// 作為隊列使用
Queue<String> queue = new LinkedList<>();
queue.offer("First");
queue.offer("Second");
String first = queue.poll(); // 移除并返回頭部元素
3.3.2 PriorityQueue
基于優先級堆的無界優先級隊列,元素按照自然順序或Comparator排序。
java
// 使用示例
Queue<Integer> priorityQueue = new PriorityQueue<>();
priorityQueue.offer(5);
priorityQueue.offer(1);
priorityQueue.offer(3);// 出隊順序是1,3,5
while (!priorityQueue.isEmpty()) {System.out.println(priorityQueue.poll());
}
3.3.3 ArrayDeque
基于循環數組實現的雙端隊列,比LinkedList更高效。適合用作棧或隊列。
java
// 作為棧使用
Deque<String> stack = new ArrayDeque<>();
stack.push("First");
stack.push("Second");
String top = stack.pop(); // "Second"// 作為隊列使用
Deque<String> queue = new ArrayDeque<>();
queue.offer("First");
queue.offer("Second");
String first = queue.poll(); // "First"
3.3.4 BlockingQueue實現
java.util.concurrent包提供了多種阻塞隊列實現:
-
ArrayBlockingQueue:有界阻塞隊列
-
LinkedBlockingQueue:可選有界阻塞隊列
-
PriorityBlockingQueue:無界優先級阻塞隊列
-
SynchronousQueue:不存儲元素的阻塞隊列
-
DelayQueue:元素延遲到期的無界阻塞隊列
java
// 使用示例
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(10);
// 生產者線程
new Thread(() -> {try {blockingQueue.put("Message");} catch (InterruptedException e) {Thread.currentThread().interrupt();}
}).start();// 消費者線程
new Thread(() -> {try {String message = blockingQueue.take();System.out.println("Received: " + message);} catch (InterruptedException e) {Thread.currentThread().interrupt();}
}).start();
3.4 Map實現類
3.4.1 HashMap
基于哈希表的Map實現,允許null鍵和null值。特點:
-
鍵無序
-
基本操作時間復雜度為O(1)
-
初始容量和負載因子影響性能
java
// 使用示例
Map<String, Integer> map = new HashMap<>();
map.put("Alice", 25);
map.put("Bob", 30);
map.put("Charlie", 28);// 獲取值
int age = map.get("Bob"); // 30// 遍歷
map.forEach((name, age) -> System.out.println(name + " is " + age + " years old"));
3.4.2 LinkedHashMap
繼承自HashMap,同時維護了一個雙向鏈表來保持插入順序或訪問順序。特點:
-
可以預測的迭代順序
-
性能略低于HashMap
-
適合需要保持插入/訪問順序的場景
java
// 保持插入順序
Map<String, Integer> linkedMap = new LinkedHashMap<>();
linkedMap.put("First", 1);
linkedMap.put("Second", 2);
linkedMap.put("Third", 3);// 遍歷順序與插入順序一致
linkedMap.forEach((k, v) -> System.out.println(k));// 創建LRU緩存
Map<String, Integer> lruCache = new LinkedHashMap<>(16, 0.75f, true) {@Overrideprotected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) {return size() > 3;}
};
3.4.3 TreeMap
基于紅黑樹實現的NavigableMap,鍵按照自然順序或Comparator排序。特點:
-
鍵有序
-
基本操作時間復雜度為O(log n)
-
不允許null鍵(除非Comparator允許)
java
// 使用示例
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Orange", 2);
treeMap.put("Apple", 5);
treeMap.put("Banana", 3);// 遍歷順序是Apple, Banana, Orange
treeMap.forEach((fruit, count) -> System.out.println(fruit + ": " + count));
3.4.4 Hashtable
線程安全的Map實現,方法都使用synchronized修飾。已逐漸被HashMap和ConcurrentHashMap取代。
java
// 使用示例
Hashtable<String, Integer> table = new Hashtable<>();
table.put("One", 1);
table.put("Two", 2);
3.4.5 ConcurrentHashMap
線程安全的高性能HashMap實現,使用分段鎖技術(JDK 8后改為CAS+synchronized)。特點:
-
高并發性能
-
不允許null鍵和null值
-
操作通常不需要鎖定整個表
java
// 使用示例
ConcurrentMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("Key1", 1);
concurrentMap.putIfAbsent("Key1", 2); // 不會替換已有值// 原子更新
concurrentMap.compute("Key1", (k, v) -> v == null ? 0 : v + 1);
3.4.6 EnumMap
專為枚舉鍵設計的Map實現,內部使用數組存儲,非常緊湊和高效。
java
// 使用示例
enum Day { MONDAY, TUESDAY, WEDNESDAY }
Map<Day, String> enumMap = new EnumMap<>(Day.class);
enumMap.put(Day.MONDAY, "First day");
3.4.7 IdentityHashMap
使用==而不是equals比較鍵的Map實現,適合需要對象標識而非對象值相等的場景。
java
// 使用示例
Map<String, Integer> identityMap = new IdentityHashMap<>();
String key1 = new String("key");
String key2 = new String("key");
identityMap.put(key1, 1);
identityMap.put(key2, 2); // 兩個不同的鍵
System.out.println(identityMap.size()); // 2
3.4.8 WeakHashMap
使用弱引用作為鍵的Map實現,當鍵不再被普通引用時,條目會被自動移除。適合實現緩存。
java
// 使用示例
Map<Object, String> weakMap = new WeakHashMap<>();
Object key = new Object();
weakMap.put(key, "value");
key = null; // 使鍵只被弱引用持有
System.gc(); // 垃圾回收后,條目可能會被移除
四、集合工具類
4.1 Collections工具類
java.util.Collections
提供了大量靜態方法,用于操作或返回集合。
4.1.1 排序和搜索
java
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);// 排序
Collections.sort(numbers); // [1, 1, 3, 4, 5, 9]// 自定義排序
Collections.sort(numbers, Comparator.reverseOrder()); // [9, 5, 4, 3, 1, 1]// 二分查找(列表必須已排序)
int index = Collections.binarySearch(numbers, 4); // 2// 隨機打亂
Collections.shuffle(numbers);
4.1.2 不可變集合
java
// 創建不可變集合
List<String> immutableList = Collections.unmodifiableList(new ArrayList<>());
Set<Integer> immutableSet = Collections.unmodifiableSet(new HashSet<>());
Map<String, Integer> immutableMap = Collections.unmodifiableMap(new HashMap<>());// JDK 9新增的工廠方法
List<String> list = List.of("a", "b", "c");
Set<Integer> set = Set.of(1, 2, 3);
Map<String, Integer> map = Map.of("a", 1, "b", 2);
4.1.3 同步集合
java
// 創建線程安全集合
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
Set<Integer> syncSet = Collections.synchronizedSet(new HashSet<>());
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
4.1.4 其他實用方法
java
// 填充
List<String> list = new ArrayList<>(Collections.nCopies(3, "default"));
Collections.fill(list, "new"); // ["new", "new", "new"]// 頻率統計
int freq = Collections.frequency(list, "new"); // 3// 極值
Integer max = Collections.max(numbers);
Integer min = Collections.min(numbers);// 替換所有
Collections.replaceAll(list, "new", "old");// 反轉
Collections.reverse(list);// 旋轉(將后兩個元素移到前面)
Collections.rotate(list, 2);
4.2 Arrays工具類
java.util.Arrays
提供了操作數組的實用方法,許多方法與Collections類似。
java
// 數組轉List(返回的List是固定大小的)
List<String> list = Arrays.asList("a", "b", "c");// 排序
int[] numbers = {3, 1, 4, 1, 5, 9};
Arrays.sort(numbers); // [1, 1, 3, 4, 5, 9]// 二分查找
int index = Arrays.binarySearch(numbers, 4); // 3// 填充
Arrays.fill(numbers, 0); // [0, 0, 0, 0, 0, 0]// 比較
int[] copy = Arrays.copyOf(numbers, numbers.length);
boolean equal = Arrays.equals(numbers, copy); // true// 字符串表示
String str = Arrays.toString(numbers); // "[0, 0, 0, 0, 0, 0]"// 并行排序(大數據量時性能更好)
Arrays.parallelSort(new int[] {5, 3, 9, 1});// JDK 8新增的Stream支持
int sum = Arrays.stream(numbers).sum();
五、集合的迭代與遍歷
5.1 迭代器模式
Java集合框架基于迭代器模式,提供了一種統一的方法來遍歷各種集合。
5.1.1 Iterator接口
java
public interface Iterator<E> {boolean hasNext();E next();default void remove() { throw new UnsupportedOperationException(); }default void forEachRemaining(Consumer<? super E> action) { ... }
}
基本用法:
java
List<String> list = Arrays.asList("A", "B", "C");
Iterator<String> it = list.iterator();
while (it.hasNext()) {String element = it.next();System.out.println(element);if (element.equals("B")) {it.remove(); // 移除當前元素}
}
5.1.2 ListIterator接口
ListIterator
擴展了Iterator
,支持雙向遍歷和修改操作。
java
public interface ListIterator<E> extends Iterator<E> {boolean hasNext();E next();boolean hasPrevious();E previous();int nextIndex();int previousIndex();void remove();void set(E e);void add(E e);
}
使用示例:
java
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
ListIterator<String> lit = list.listIterator();// 正向遍歷
while (lit.hasNext()) {System.out.println(lit.next());
}// 反向遍歷
while (lit.hasPrevious()) {String element = lit.previous();System.out.println(element);if (element.equals("B")) {lit.set("B-Updated"); // 修改當前元素}
}
5.1.3 快速失敗(fail-fast)機制
大多數集合的迭代器實現了快速失敗機制,當在迭代過程中檢測到集合被修改(除了通過迭代器自己的remove方法),會拋出ConcurrentModificationException
。
java
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));// 會拋出ConcurrentModificationException
for (String s : list) {if (s.equals("B")) {list.remove(s); // 錯誤的方式}
}// 正確的修改方式
Iterator<String> it = list.iterator();
while (it.hasNext()) {if (it.next().equals("B")) {it.remove(); // 通過迭代器移除}
}
5.2 遍歷集合的各種方式
5.2.1 傳統的for循環(適合List)
java
List<String> list = Arrays.asList("A", "B", "C");
for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));
}
5.2.2 增強的for-each循環
java
for (String s : list) {System.out.println(s);
}
5.2.3 使用Iterator
java
Iterator<String> it = list.iterator();
while (it.hasNext()) {System.out.println(it.next());
}
5.2.4 使用ListIterator
java
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {System.out.println(lit.next());
}
5.2.5 使用Java 8的forEach方法
java
list.forEach(System.out::println);// 或使用Lambda表達式
list.forEach(s -> System.out.println(s));
5.2.6 使用Stream API
java
list.stream().forEach(System.out::println);// 并行處理
list.parallelStream().forEach(System.out::println);
5.3 Spliterator接口
JDK 8引入的Spliterator
(可分迭代器)用于并行遍歷和分割元素序列。
java
public interface Spliterator<T> {boolean tryAdvance(Consumer<? super T> action);Spliterator<T> trySplit();long estimateSize();int characteristics();
}
使用示例:
java
List<String> list = Arrays.asList("A", "B", "C", "D", "E");
Spliterator<String> spliterator = list.spliterator();// 嘗試分割
Spliterator<String> half = spliterator.trySplit();// 處理前半部分
half.forEachRemaining(System.out::println); // 輸出 A, B// 處理后半部分
spliterator.forEachRemaining(System.out::println); // 輸出 C, D, E
六、集合的性能比較與選擇
6.1 主要集合類的性能特征
集合類型 | 實現類 | 獲取 | 插入/刪除 | 包含 | 有序 | 線程安全 | 允許null |
---|---|---|---|---|---|---|---|
List | ArrayList | O(1) | O(n) | O(n) | 插入順序 | 否 | 是 |
List | LinkedList | O(n) | O(1) | O(n) | 插入順序 | 否 | 是 |
List | Vector | O(1) | O(n) | O(n) | 插入順序 | 是 | 是 |
Set | HashSet | O(1) | O(1) | O(1) | 無 | 否 | 是(1個) |
Set | LinkedHashSet | O(1) | O(1) | O(1) | 插入順序 | 否 | 是(1個) |
Set | TreeSet | O(log n) | O(log n) | O(log n) | 排序順序 | 否 | 否 |
Queue | PriorityQueue | O(1) peek | O(log n) offer | - | 排序順序 | 否 | 否 |
Map | HashMap | O(1) | O(1) | O(1) | 無 | 否 | 是(1個鍵) |
Map | LinkedHashMap | O(1) | O(1) | O(1) | 插入/訪問順序 | 否 | 是(1個鍵) |
Map | TreeMap | O(log n) | O(log n) | O(log n) | 排序順序 | 否 | 否(鍵) |
Map | Hashtable | O(1) | O(1) | O(1) | 無 | 是 | 否 |
Map | ConcurrentHashMap | O(1) | O(1) | O(1) | 無 | 是 | 否 |
6.2 如何選擇合適的集合
選擇集合時應考慮以下因素:
-
元素是否需要有序:
-
需要保持插入順序:LinkedHashSet、LinkedHashMap、ArrayList、LinkedList
-
需要排序:TreeSet、TreeMap、PriorityQueue
-
不需要順序:HashSet、HashMap
-
-
是否需要允許重復元素:
-
允許重復:List及其實現類
-
不允許重復:Set及其實現類
-
-
性能需求:
-
隨機訪問多:ArrayList
-
插入刪除多:LinkedList
-
快速查找:HashSet/HashMap
-
-
線程安全需求:
-
需要線程安全:ConcurrentHashMap、CopyOnWriteArrayList、Collections.synchronizedXxx
-
不需要線程安全:標準實現類
-
-
null值處理:
-
需要存儲null:ArrayList、HashSet、HashMap等
-
不能存儲null:Hashtable、ConcurrentHashMap、EnumSet/Map等
-
-
內存占用:
-
內存敏感:ArrayList比LinkedList更節省空間
-
EnumSet/Map非常緊湊高效
-
6.3 集合初始容量與負載因子
對于基于哈希表的集合(HashMap、HashSet等),初始容量和負載因子影響性能:
-
初始容量:哈希表創建時的桶數量。默認16。
-
負載因子:哈希表自動擴容前的填充比例。默認0.75。
java
// 自定義初始容量和負載因子
Map<String, Integer> map = new HashMap<>(32, 0.5f);
合理設置這些參數可以減少rehash操作,提高性能:
-
如果知道元素數量,設置初始容量為預期數量的1.33倍(1/0.75)
-
高負載因子減少內存使用但增加查找成本
-
低負載因子提高查找性能但增加內存使用
七、Java 8/9/10對集合的增強
7.1 Java 8的增強
7.1.1 Stream API
Stream API為集合提供了強大的數據操作能力:
java
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");// 過濾和映射
List<String> result = names.stream().filter(name -> name.length() > 4).map(String::toUpperCase).collect(Collectors.toList());// 統計
long count = names.stream().filter(name -> name.startsWith("A")).count();// 并行處理
int totalLength = names.parallelStream().mapToInt(String::length).sum();
7.1.2 forEach方法
所有集合都新增了forEach
方法:
java
Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);// 遍歷Map
map.forEach((k, v) -> System.out.println(k + "=" + v));
7.1.3 Map的增強方法
Map接口新增了許多實用方法:
java
Map<String, Integer> map = new HashMap<>();// 鍵不存在時才放入
map.putIfAbsent("A", 1);// 根據鍵計算新值
map.compute("A", (k, v) -> v == null ? 0 : v + 1);// 合并值
map.merge("A", 1, (oldVal, newVal) -> oldVal + newVal);// 獲取或默認值
int val = map.getOrDefault("B", 0);
7.1.4 Collection的removeIf方法
java
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));// 移除所有偶數
numbers.removeIf(n -> n % 2 == 0); // [1, 3, 5]
7.2 Java 9的增強
7.2.1 不可變集合的工廠方法
Java 9引入了簡潔的創建不可變集合的方法:
java
// 不可變List
List<String> immutableList = List.of("a", "b", "c");// 不可變Set
Set<Integer> immutableSet = Set.of(1, 2, 3);// 不可變Map
Map<String, Integer> immutableMap = Map.of("a", 1, "b", 2);// 更多元素的Map
Map<String, Integer> map = Map.ofEntries(Map.entry("a", 1),Map.entry("b", 2),Map.entry("c", 3)
);
這些集合:
-
不允許null元素
-
創建后不可修改
-
空間優化(可能使用專用內部類)
7.2.2 其他增強
-
Optional
增加了stream()
方法 -
新增
Collectors.filtering
和Collectors.flatMapping
7.3 Java 10的增強
7.3.1 不可變集合的copyOf方法
java
List<String> original = new ArrayList<>();
List<String> copy = List.copyOf(original); // 不可變副本
7.3.2 var關鍵字與集合
雖然var不是集合特有的,但它簡化了集合聲明:
java
var list = new ArrayList<String>(); // 推斷為ArrayList<String>
var map = new HashMap<Integer, String>(); // 推斷為HashMap<Integer, String>
八、集合的最佳實踐
8.1 通用最佳實踐
-
使用接口類型聲明集合:
java
// 好 List<String> list = new ArrayList<>(); // 不好 ArrayList<String> list = new ArrayList<>();
-
預估集合大小:
java
// 如果知道大約需要存儲1000個元素 List<String> list = new ArrayList<>(1000);
-
使用isEmpty()而不是size()==0:
java
// 好 if (list.isEmpty()) { ... } // 不好 if (list.size() == 0) { ... }
-
優先使用for-each循環:
java
for (String s : list) { ... }
-
注意集合的equals和hashCode:
-
不同集合類的equals行為不同
-
修改作為Map鍵的對象時要小心
-
8.2 并發場景下的最佳實踐
-
選擇合適的并發集合:
-
高并發讀取:
ConcurrentHashMap
-
寫少讀多:
CopyOnWriteArrayList
-
阻塞隊列:
ArrayBlockingQueue
等
-
-
避免在迭代時修改集合:
-
使用并發集合或同步塊
-
或者先創建副本再迭代
-
-
注意原子復合操作:
java
// 非原子操作 if (!map.containsKey(key)) {map.put(key, value); }// 使用原子方法 map.putIfAbsent(key, value);
8.3 性能優化建議
-
選擇合適的集合類型:
-
隨機訪問多:ArrayList
-
插入刪除多:LinkedList
-
-
合理設置HashMap/HashSet初始容量:
java
// 預期存儲1000個元素,負載因子0.75 Map<String, Integer> map = new HashMap<>(1333); // 1000 / 0.75
-
考慮使用EnumSet/EnumMap:
-
枚舉集合非常高效
-
-
避免頻繁的裝箱拆箱:
-
考慮使用Trove、FastUtil等第三方庫
-
-
批量操作優于單元素操作:
java
// 好 list.addAll(otherList); // 不好 for (String s : otherList) {list.add(s); }
8.4 常見陷阱與避免方法
-
ConcurrentModificationException:
-
避免在迭代時直接修改集合
-
使用迭代器的remove方法或并發集合
-
-
可變對象作為Map鍵:
-
如果鍵對象可變,修改后可能導致找不到值
-
建議使用不可變對象作為鍵
-
-
equals和hashCode不一致:
-
確保相等的對象有相同的hashCode
-
重寫equals時必須重寫hashCode
-
-
原始集合的泛型使用:
-
避免使用原始類型集合
-
總是指定泛型參數
-
-
內存泄漏:
-
長期存活的集合可能導致內存泄漏
-
考慮使用WeakHashMap或定期清理
-
九、集合框架的擴展與替代方案
9.1 第三方集合庫
9.1.1 Google Guava
Google的Guava庫提供了豐富的集合工具:
java
// 不可變集合
ImmutableList<String> immutableList = ImmutableList.of("a", "b", "c");// 多值Map
Multimap<String, Integer> multimap = ArrayListMultimap.create();
multimap.put