集合框架是 Java 開發最高頻的基礎設施。許多開發者長期只用 ArrayList,對 HashMap、HashSet、TreeMap 等底層原理理解不足,導致性能隱患或面試失利。本文從源碼視角,系統梳理三大核心接口及其主流實現類的結構特點、擴容機制、線程安全性與選型建議。
Object[] elementData,默認初始容量 10(延遲初始化為空數組,首次添加擴容為 10)。grow()):newCapacity = oldCapacity + (oldCapacity >> 1),即 1.5 倍。超過 MAX_ARRAY_SIZE (Integer.MAX_VALUE - 8) 則分配 Integer.MAX_VALUE。get(index):O(1) 直接下標訪問。add(E e):均攤 O(1),觸發擴容時 O(n) 需 Arrays.copyOf。remove(index):O(n) 需 System.arraycopy 移動後續元素。initialCapacity 避免多次擴容拷貝;多線程環境用 Collections.synchronizedList 或 CopyOnWriteArrayList。Node,含 item、next、prev;維護 first/last 指針及 size。addFirst/addLast:O(1) 僅調整指針。get(index):O(n) 需從頭/尾遍歷(源碼優化:index < size/2 從頭,否則從尾)。remove(Object):O(n) 先查找再斷鏈。Deque 接口,可直接作為棧、隊列、雙端隊列使用(push/pop、offer/poll)。Node[] table 、size、threshold、loadFactor (默認 0.75)。hash(key) = key.hashCode() ^ (key.hashCode() >>> 16),高低位混合減少碰撞。index = (n - 1) & hash(n 為 2 的冪)。equals 命中則覆蓋值;否則尾插。TreeNode),查詢從 O(n) 降為 O(log n)。resize() 2 倍擴容,重新計算索引(JDK 1.8 優化:節點要么留原位,要么移至 oldCap + index,避免全量 rehash)。synchronized,粗粒度鎖,並發度極低。NavigableMap。Comparable)或構造傳入 Comparator。put/get/remove/firstKey/lastKey/lowerKey/higherKey 等範圍查詢極其高效。PRESENT 單例private transient HashMap map;
public boolean add(E e) { return map.put(e, PRESENT) == null; }
public TreeSet() { this(new TreeMap()); }
public boolean add(E e) { return m.put(e, PRESENT) == null; }
first()/last()/lower()/higher()/subSet() 等導航操作。| 需求 | 首選實現 | 備選/注意 |
|---|---|---|
| 頻繁隨機訪問、末尾增刪 | ArrayList | 預估大小給 initialCapacity |
| 頻繁頭尾增刪、棧/隊列 | LinkedList | 避免隨機訪問 |
| 高性能鍵值存取、無序 | HashMap | 並發用 ConcurrentHashMap |
| 鍵值有序遍歷、範圍查詢 | TreeMap | Key 需實現 Comparable 或傳 Comparator |
| 去重、無序 | HashSet | |
| 去重、有序遍歷 | TreeSet | |
| 去重、保持插入順序 | LinkedHashSet | |
| 線程安全 List | CopyOnWriteArrayList | 讀多寫少,寫入複製數組 |
| 線程安全 Map | ConcurrentHashMap | JDK 1.8 CAS+synchronized 細粒度鎖 |
| 線程安全 Set | ConcurrentHashMap.newKeySet() | 或 Collections.synchronizedSet |
java.util.concurrent 包對應實現。吃透底層原理,才能在業務開發中做最優選型,在面試中游刃有餘。