在Java集合框架中,CollectionMap是两大核心接口,它们并列存在而非继承关系。许多开发者混淆两者,导致在实际开发中选择错误的集合类型。本文将从JDK 1.8+源码底层深度剖析CollectionMap接口的所有实现类,揭示其算法逻辑,并总结最佳实践与避坑指南。所有内容均基于官方源码,确保技术准确性。


一、Java集合框架核心关系图

CollectionMap是Java集合框架的两个独立顶层接口,它们没有继承关系,而是并列存在。以下是完整的继承关系图: Collection:


二、Collection接口实现类底层详解

1. ArrayList:基于数组的动态集合

核心结构transient Object[] elementData;(默认容量10)

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private transient Object[] elementData;
    private int size;
    
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = e;
        return true;
    }

    private void ensureCapacityInternal(int minCapacity) {
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容1.5倍
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
}

算法逻辑

  • 扩容:默认容量10,扩容时 newCapacity = oldCapacity * 1.5
  • 时间复杂度
    • add(E e):均摊 O(1)(扩容时O(n))
    • get(int index):O(1)(数组随机访问)
    • remove(int index):O(n)(需移动后续元素)
  • 适用场景随机访问频繁(如遍历、按索引查询)

2. LinkedList:基于双向链表的集合

核心结构private static class Node<E> { E item; Node<E> next; Node<E> prev; }

public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
    
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    
    private void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
    }
}

算法逻辑

  • 链表操作:每个节点包含 prevnext 指针
  • 时间复杂度
    • add(E e):O(1)(尾部添加)
    • get(int index):O(n)(需遍历)
    • remove(int index):O(n)(需定位节点)
  • 适用场景频繁插入/删除(尤其是头部/尾部)

3. HashSet:基于 HashMap 的无序集合

核心结构private transient HashMap<E,Object> map;key 为集合元素,valuePRESENT

public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable {
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();
    
    public HashSet() {
        map = new HashMap<>();
    }
    
    public boolean add(E e) {
        return map.put(e, PRESENT) == null; // 实际调用HashMap的put
    }
}

算法逻辑

  • 哈希冲突处理:使用 HashMap 的链表+红黑树(JDK 8+)
  • 时间复杂度
    • add(E e):均摊 O(1)
    • contains(E e):均摊 O(1)
  • 关键点元素必须正确重写 hashCode()equals()
  • 适用场景去重、快速查找

4. LinkedHashSet:基于 LinkedHashMap 的有序集合

核心结构private transient LinkedHashMap<E,Object> map;(继承自 HashSet

public class LinkedHashSet<E> extends HashSet<E> {
    public LinkedHashSet() {
        super(16, 0.75f, true); // accessOrder=true
    }
}

算法逻辑

  • 有序性:通过 LinkedHashMapaccessOrder(访问顺序)或 insertOrder(插入顺序)
  • 时间复杂度:与 HashSet 相同(均摊 O(1))
  • 适用场景插入顺序或访问顺序的去重集合(如LRU缓存)

5. TreeSet:基于 TreeMap 的有序集合

核心结构private transient TreeMap<E,Object> m;key 为集合元素,valuePRESENT

public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable {
    private transient NavigableMap<E,Object> m;
    
    public TreeSet() {
        m = new TreeMap<>();
    }
    
    public boolean add(E e) {
        return m.put(e, PRESENT) == null; // 实际调用TreeMap的put
    }
}

算法逻辑

  • 红黑树实现:元素需实现 Comparable 或提供 Comparator
  • 时间复杂度
    • add(E e):O(log n)
    • contains(E e):O(log n)
  • 适用场景排序、范围查询(如 headSet(), subSet()

6. PriorityQueue:基于堆的优先队列

核心结构transient Object[] queue;(数组实现的二叉堆)

public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable {
    private transient Object[] queue;
    private int size;
    
    public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
        if (i >= queue.length)
            grow(i + 1);
        size = i + 1;
        if (i == 0)
            queue[0] = e;
        else
            siftUp(i, e); // 堆调整(上浮)
        return true;
    }
    
    private void siftUp(int k, E x) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (comparator.compare(x, (E)e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = x;
    }
}

算法逻辑

  • 最小堆:默认最小优先级(comparator 可自定义)
  • 时间复杂度
    • offer(E e):O(log n)
    • poll():O(log n)
  • 适用场景任务调度、Top-K问题

7. ArrayDeque:基于循环数组的双端队列

核心结构transient Object[] elements;(数组+头尾指针)

public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>, java.io.Serializable {
    transient Object[] elements;
    private int head;
    private int tail;
    
    public boolean add(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[tail] = e;
        if (tail == elements.length - 1)
            tail = 0;
        else
            tail++;
        if (head == tail)
            doubleCapacity();
        return true;
    }
}

算法逻辑

  • 循环数组headtail 指针维护队列
  • 扩容:容量翻倍(newCapacity = oldCapacity * 2
  • 时间复杂度
    • addFirst(E e):O(1)
    • addLast(E e):O(1)
  • 适用场景高性能双端队列(如实现栈、队列)

8. VectorStack:过时实现(避免使用!)

  • Vector:线程安全(synchronized 方法),扩容为2倍(oldCapacity * 2
  • Stack:继承 Vector,过时(建议用 ArrayDeque 代替)

三、Map接口实现类底层详解

1. HashMap:基于哈希表的无序映射

核心结构transient Node<K,V>[] table;(数组+链表/红黑树)

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;
        // ...
    }
    
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            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 {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // 8
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

算法逻辑

  • 哈希计算hash = key.hashCode() ^ (key.hashCode() >>> 16)
  • 冲突处理:链表(<8个节点) + 红黑树(≥8个节点)
  • 扩容:容量翻倍(newCapacity = oldCapacity * 2
  • 时间复杂度
    • put(K key, V value):均摊 O(1)(链表)/ O(log n)(红黑树)
    • get(K key):均摊 O(1)
  • 关键点键必须重写 hashCode()equals()

2. LinkedHashMap:基于 HashMap 的有序映射

核心结构transient Entry<K,V> head;(双向链表维护顺序)

public class LinkedHashMap<K,V> extends HashMap<K,V> {
    static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }
    
    public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false; // 插入顺序
    }
    
    @Override
    void afterNodeAccess(Node<K,V> e) {
        LinkedHashMap.Entry<K,V> last;
        if (accessOrder && (last = tail) != e) {
            LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            p.before = p.after = null;
            if (b != null)
                b.after = a;
            else
                head = a;
            if (a != null)
                a.before = b;
            else
                last = b;
            if (last != null)
                last.after = p;
            p.before = last;
            p.after = null;
            tail = p;
            ++modCount;
        }
    }
}

算法逻辑

  • 有序性:通过 before/after 指针维护插入顺序(accessOrder=false)或访问顺序(accessOrder=true
  • 时间复杂度:与 HashMap 相同(均摊 O(1))
  • 适用场景LRU缓存(设置 accessOrder=true

3. TreeMap:基于红黑树的有序映射

核心结构private transient Entry<K,V> root;(红黑树节点)

public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable {
    static final class Entry<K,V> {
        K key;
        V value;
        Entry<K,V> left;
        Entry<K,V> right;
        Entry<K,V> parent;
        boolean color = BLACK;
        // ...
    }
    
    public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type check
            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        do {
            parent = t;
            cmp = 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);
        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;
    }

算法逻辑

  • 红黑树:自动平衡,保证 O(log n) 操作
  • 排序:元素需实现 Comparable 或提供 Comparator
  • 时间复杂度
    • put(K key, V value):O(log n)
    • get(K key):O(log n)
  • 适用场景排序键值对、范围查询(如时间区间统计)

4. Hashtable:线程安全的哈希映射(已过时)

核心结构transient Entry<K,V>[] table;(与 HashMap 类似,但加锁)

public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable {
    private transient Entry<K,V>[] table;
    
    public synchronized V put(K key, V value) {
        if (value == null)
            throw new NullPointerException();
        Entry<K,V> entry = getEntry(key);
        if (entry != null) {
            V oldValue = entry.value;
            entry.value = value;
            return oldValue;
        } else {
            addEntry(hash(key), key, value, table.length);
            return null;
        }
    }
    
    private void addEntry(int hash, K key, V value, int index) {
        Entry<K,V> e = table[index];
        table[index] = new Entry<>(hash, key, value, e);
        if (++count >= threshold)
            resize(2 * table.length);
    }
}

算法逻辑

  • 线程安全:所有方法用 synchronized 修饰(锁粒度粗,性能差)
  • 扩容:容量翻倍(newCapacity = oldCapacity * 2
  • 时间复杂度:与 HashMap 相同(O(1)),但性能低10倍+
  • 避坑已过时,现代Java应使用 ConcurrentHashMap

四、Collection vs Map 核心对比

特性Collection 接口Map 接口
数据结构单元素集合(List/Set/Queue)键值对映射(Key-Value)
核心方法add(E e), remove(E e), size()put(K key, V value), get(K key)
是否允许重复List:允许;Set:不允许Key:不允许;Value:允许
底层实现数组/链表/红黑树哈希表/红黑树
典型场景存储列表、集合、队列存储配置、缓存、关联数据
线程安全Collections.synchronizedList()ConcurrentHashMap(非 Hashtable
遍历方式iterator(), forEach()keySet(), entrySet(), values()
关键设计原则元素操作键值对操作

五、最佳实践与避坑指南

最佳实践(按场景推荐)

操作场景推荐集合原因
单元素集合ArrayList随机访问 O(1),缓存友好
频繁头尾插入/删除LinkedListO(1) 头尾操作
自动去重 + 快速查找HashSet均摊 O(1) 查找
保留插入顺序的去重LinkedHashSet插入顺序,性能略低于 HashSet
排序 + 范围查询TreeSetO(log n) 操作,自动排序
优先级任务调度PriorityQueueO(log n) 插入/删除
高性能双端队列ArrayDequeLinkedList 更快(无指针开销)
键值对快速查找HashMap均摊 O(1) 查找,无序
保留插入顺序的键值对LinkedHashMapLRU缓存实现(accessOrder=true
排序键值对TreeMapO(log n) 操作,自动按键排序
线程安全的键值对ConcurrentHashMap高性能并发(Hashtable 已过时)

️ 避坑指南(高频错误)

Collection 避坑点

  1. Vector 用于多线程

    • 错误:Vectorsynchronized 锁粒度粗,性能极低。
    • 正确:Map<String, String> map = new ConcurrentHashMap<>();ConcurrentLinkedQueue
  2. 循环中直接 remove()

    • 错误:for (E e : list) list.remove(e)ConcurrentModificationException
    • 正确:用 Iterator 遍历:
      Iterator<E> it = list.iterator();
      while (it.hasNext()) {
          if (condition) it.remove();
      }
      

Map 避坑点

  1. HashMap 键未重写 hashCode()/equals()

    • 错误:new Person("A")new Person("A") 被视为不同键。
    • 正确:确保自定义类重写 hashCode()equals()(推荐Lombok @EqualsAndHashCode)。
  2. 误用 Hashtable 代替 ConcurrentHashMap

    • 错误:Hashtablesynchronized 导致性能低下。
    • 正确:Map<String, String> map = new ConcurrentHashMap<>();
  3. 迭代时修改 Map

    • 错误:for (Map.Entry<String, String> e : map.entrySet()) map.remove(e.getKey());
    • 正确:用 IteratorremoveIf()
      map.entrySet().removeIf(e -> e.getKey().equals("target"));
      
  4. TreeMap 未实现 Comparable

    • 错误:TreeMap<CustomObject, String>ClassCastException
    • 正确:提供 Comparator 或让类实现 Comparable
  5. 忽略 HashMap 的扩容

    • 错误:默认容量16,频繁添加触发扩容(影响性能)。
    • 正确:初始化时指定容量:new HashMap<>(1024)

六、总结

关键原则

  1. Collection vs Map

    • Collection 处理元素(如 List),Map 处理键值对(如 HashMap)。
    • Map 不继承 Collection,这是Java集合框架的基石设计。
  2. 选择集合的黄金法则

    • 需要元素操作 → 选 Collection 实现类。
    • 需要键值关联 → 选 Map 实现类。
    • 线程安全:用 ConcurrentHashMap 代替 Hashtable
  3. 性能与正确性

    • HashMap/HashSet必须重写 hashCode()equals()
    • TreeMap/TreeSet必须保证键的排序ComparableComparator)。
    • ConcurrentHashMap避免在迭代时修改(用 computeIfPresent 等方法)。

参考资料

  • JDK 1.8 源码
  • 《Effective Java》第3版:Item 30-33(集合相关)
  • Java集合框架官方文档:Java Collections Framework
  • Java并发包文档:ConcurrentHashMap
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com