HashMap

HashMap = 哈希表实现的键值对集合,key 唯一、value 可重复、允许 null key/value,非线程安全,增删查平均 O(1)

创建(3 种)

// 1. 默认初始容量 16,负载因子 0.75
Map<String, Integer> map = new HashMap<>();

// 2. 指定初始容量(减少 rehash)
Map<String, Integer> map = new HashMap<>(100);

// 3. 通过已有 Map 快速构造
Map<String, Integer> map = new HashMap<>(existingMap);

基本 CRUD

map.put("apple", 3);          // 增/改(key 存在则覆盖)
map.get("apple");             // 查,返回 value 或 null
map.containsKey("apple");     // 判断 key 存在
map.containsValue(3);         // 判断 value 存在
map.remove("apple");          // 删,返回旧值或 null
map.size();                   // 键值对个数
map.isEmpty();                // 是否空
map.clear();                  // 清空

遍历(4 样)

// 1. entrySet(最常用,一次拿 key+value)
for (Map.Entry<String, Integer> e : map.entrySet()) {
    System.out.println(e.getKey() + "=" + e.getValue());
}

// 2. keySet / values
for (String k : map.keySet()) System.out.println(k);
for (Integer v : map.values()) System.out.println(v);

// 3. Lambda(Java 8+)
map.forEach((k, v) -> System.out.println(k + "=" + v));

// 4. Iterator(遍历中删除安全)
Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry<String, Integer> e = it.next();
    if (e.getValue() < 1) it.remove();
}

Java 8+ 常用快捷 API

map.getOrDefault("orange", 0);                 // 不存在返回默认值

map.putIfAbsent("orange", 0);                  // 不存在才 put

map.merge("apple", 1, Integer::sum);           // value 累加:apple=旧值+1

map.computeIfAbsent("banana", k -> loadFromDB(k)); // 不存在则计算并放入

map.computeIfPresent("apple", (k, v) -> v > 5 ? null : v + 1); // 存在则更新/删除

批量操作

Map<String, Integer> map2 = Map.of("k1", 1, "k2", 2); // Java 9 不可变 map
map.putAll(map2);                                       // 批量放入

线程安全替代

// 1. 同步包装(读写全串行)
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());

// 2. 高并发
ConcurrentHashMap<String, Integer> cmap = new ConcurrentHashMap<>();
cmap.merge("key", 1, Integer::sum); // 原子复合操作

背口诀

HashMap 键值 O(1),put/get 常用 API;遍历 entrySet 最高效,Java 8 merge 计算快;线程安全用 ConcurrentHashMap”。

HashMap源码

HashMap 的底层逻辑 = “数组+链表+红黑树+哈希扰动+扩容重散列” 五件套,
目标:O(1) 平均读写、高空间利用率、线程不安全、可动态长大

下面按 哈希→寻址→冲突→树化→扩容 一路跟踪 putVal 源码

哈希扰动:让高位也参与寻址,减少碰撞

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

^ (h >>> 16) 把高 16 位拉下来与低 16 位异或,散列更均匀

成员变量:一眼看懂容量/阈值/树化门槛

transient Node<K,V>[] table;     // 哈希桶数组
transient int size;              // 实际键值对数
int threshold;                   // 扩容阈值 = capacity * loadFactor
final float loadFactor;          // 默认 0.75
static final int TREEIFY_THRESHOLD = 8;   // 链表转红黑树门槛
static final int UNTREEIFY_THRESHOLD = 6; // 退化门槛
static final int MIN_TREEIFY_CAPACITY = 64; // 树化前必须先扩容到 64

懒初始化:第一次 put 才扩容(省内存)

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;
    // (1) 空表→扩容到 16(或构造指定值)
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    // (2) 寻址:(n - 1) & hash 永远落在 [0, n-1]
    i = (n - 1) & hash;
    p = tab[i];

冲突处理:链表尾插 → 红黑树

    if (p == 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);             // 树化(≥64 才转树)
                    break;
                }
                if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
                    break;        // 找到相同 key
                p = e;
            }
        }
        if (e != null) {          // 旧 key 存在 → 替换 value
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);   // 给 LinkedHashMap 用的回调
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)       // 元素个数 > 阈值
        resize();                 // 2 倍扩容 + 重散列
    afterNodeInsertion(evict);
    return null;                  // 返回 null 表示新增
}

扩容:2 倍连续数组 + 重散列(高位拆分技巧)

final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int newCap = oldCap << 1;   // *2
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    threshold = (int)(newCap * loadFactor);
    table = newTab;
    if (oldTab != null) {
        for (Node<K,V> e : oldTab) {          // 遍历旧桶
            while (e != null) {
                Node<K,V> next = e.next;
                // 高位拆分:同一条链表分成“低半段”和“高半段”两个桶
                if ((e.hash & oldCap) == 0)   // 原索引
                    e.next = newTab[i];
                else                          // 原索引 + oldCap
                    e.next = newTab[i + oldCap];
                newTab[i] = e;
                e = next;
            }
        }
    }
    return newTab;
}

不需要重新计算 hash,用 (hash & oldCap) 判断高位是 0 还是 1,
一条链表直接拆成两条,CPU 缓存友好

红黑树退化

  • 链表长度 ≤ 6resize 时 退成链表

  • 防止 哈希攻击(大量同 hash 字符串)时链表过长

线程安全 & Fail-Fast

  • 全程 无锁,多线程并发 put 会 死链/丢数据 → 用 ConcurrentHashMap

  • modCount 计数,迭代器发现变动抛 ConcurrentModificationExceptio

设计思想一句话

    哈希扰动降冲突,数组定位 O(1);链表尾插防死链,≥8&≥64 才树化;2 倍扩容高位拆,重散列缓存友好;无锁快速失败,多线程请换 ConcurrentHashMap

    LinkedHashMap

    LinkedHashMap = 哈希表 + 双向链表
    既保留 HashMapO(1) 增删查,又支持 插入顺序访问顺序(LRU 基础),key 唯一、允许 null,非线程安全

    创建(3 种)

    // 1. 默认:插入顺序(最常用)
    Map<String, Integer> map = new LinkedHashMap<>();
    
    // 2. 指定初始容量
    Map<String, Integer> map = new LinkedHashMap<>(100);
    
    // 3. 访问顺序模式(第 3 个参数 true → LRU)
    Map<String, Integer> lru = new LinkedHashMap<>(16, 0.75f, true);

    基本 CRUD(与 HashMap 完全一致)

    map.put("apple", 3);          // 增/改
    map.get("apple");             // 查
    map.containsKey("apple");     // 判断 key
    map.remove("apple");          // 删
    map.size();                   // 个数
    map.isEmpty();                // 空判断
    map.clear();                  // 清空

    顺序特性演示

    Map<String, Integer> map = new LinkedHashMap<>();
    map.put("A", 1);
    map.put("B", 2);
    map.put("C", 3);
    
    map.forEach((k, v) -> System.out.print(k)); // 输出顺序:A B C(插入序)

    访问顺序模式(LRU 雏形)

    Map<String, Integer> lru = new LinkedHashMap<>(16, 0.75f, true);
    lru.put("A", 1);
    lru.put("B", 2);
    lru.put("C", 3);
    lru.get("A");                 // 访问 A
    lru.forEach((k, v) -> System.out.print(k)); // 输出:B C A(最近访问放最后)

    遍历(顺序保证)

    // 1. for-each 插入序/访问序
    for (Map.Entry<String, Integer> e : map.entrySet()) {
        System.out.println(e.getKey() + "=" + e.getValue());
    }
    
    // 2. Lambda
    map.forEach((k, v) -> System.out.println(k + "=" + v));

    批量操作

    Map<String, Integer> map2 = Map.of("k1", 1, "k2", 2); // Java 9 不可变
    map.putAll(map2);                                       // 批量放入

    线程安全替代

    // 1. 同步包装(保序)
    Map<String, Integer> syncMap = Collections.synchronizedMap(new LinkedHashMap<>());
    
    // 2. 高并发且需顺序
    Map<String, Integer> cow = new ConcurrentHashMap<>();
    // 或 ConcurrentSkipListMap(红黑树顺序)

    背口诀

    LinkedHashMap 去重又保序,插入顺序默认齐;访问顺序构造传 true,一行代码实现 LRU”。

    LinkedHashMap源码

    LinkedHashMap = HashMap 的全部能力 + 双向链表保序
    节点带 before/after 指针,插入/访问/删除都维护链表,迭代直接遍历链表(O(n) 且缓存友好),支持 插入顺序LRU 访问顺序,其余增删查仍复用父类 HashMap 的桶逻辑。

    下面按 节点结构 → 链表维护 → 顺序模式 → 迭代器 → 性能与内存 5 段源码级解读

    节点结构:在 HashMap.Node 基础上再加两个指针

    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); // 保留桶里单链表/树指针
        }
    }

    一个对象同时属于 两条链
    next/parent 负责桶内哈希冲突链/树
    before/after 负责全局 双向链表(插入序或 LRU 序)

    链表头尾指针:全局变量直接定位

    transient LinkedHashMap.Entry<K,V> head;  // 链表头
    transient LinkedHashMap.Entry<K,V> tail;  // 链表尾
    final boolean accessOrder;                // false=插入序(默认),true=访问序

    空表时 head = tail = null;单节点时两者指向同一 Entry。

    链表维护:HashMap 预留的“回调钩子”被 LinkedHashMap 重写

    回调钩子 用途 LinkedHashMap 实现
    afterNodeAccess(e) 访问元素后 若为 访问序 把节点移到尾部
    afterNodeInsertion(evict) 新增元素后 可在这里淘汰最老节点(LRU)
    afterNodeRemoval(e) 删除元素后 把节点从双向链表摘掉

    插入回调(尾插法,保插入序)

    void afterNodeInsertion(boolean evict) { // evict 由 HashMap 构造参数传入
        LinkedHashMap.Entry<K,V> first;
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            // 子类可重写 removeEldestEntry 做容量控制
            removeNode(hash(first.key), first.key, null, false, true);
        }
    }
    
    private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        LinkedHashMap.Entry<K,V> last = tail;
        tail = p;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
    }

    访问回调(访问序才生效)

    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;
            LinkedHashMap.Entry<K,V> b = p.before, a = p.after;
            p.after = null;          // 先摘下
            if (b == null) head = a; else b.after = a;
            if (a != null) a.before = b; else last = b;
            // 再尾插
            p.before = last;
            if (last != null) last.after = p; else head = p;
            tail = p;
        }
    }

    每次 get/put 已存在 key 都会触发 afterNodeAccess把节点移到尾部 = 最近使用

    迭代器:不再遍历桶,直接沿链表走

    final LinkedHashMap.Entry<K,V> nextNode() {
        LinkedHashMap.Entry<K,V> e = next;
        if (modCount != expectedModCount) throw new ConcurrentModificationException();
        next = e.after;   // 顺着 after 指针一路向后
        return e;
    }

    迭代时间与 size 成正比,且 内存连续访问(Node 对象在堆中相邻概率高),CPU 缓存友好 → 比 HashSet/HashMap 的“桶跳跃”遍历快。

    性能与内存开销

    维度 HashMap LinkedHashMap
    增删查 O(1) 均摊 同 HashMap(多几次指针赋值,可忽略)
    迭代 桶跳跃,缓存不友好 链表顺序,缓存友好
    内存 1 个 Node Node + 2 个指针(24 byte 64 位压缩)
    树化/退化 与父类完全一致 同一套逻辑

    典型用法:一行实现 LRU

    Map<String, Object> lru = new LinkedHashMap<>(16, 0.75f, true) {
        protected boolean removeEldestEntry(Map.Entry eldest) {
            return size() > MAX_ENTRIES; // 超过容量淘汰最老
        }
    };

    访问序 + removeEldestEntry 回调 = 标准 LRU 缓存模板

    设计思想一句话

    在 HashMap 节点上长两条链:桶链解决哈希冲突,before/after 链保全局顺序;重写父类三个回调,插入/访问/删除时维护链表;迭代直接沿链表跑,缓存友好;加 2 个指针换来有序遍历 + LRU 能力。

    ConcurrentHashMap

    ConcurrentHashMap = 高并发、线程安全的哈希表
    JDK 8 起采用 "数组 + 链表 + 红黑树 + CAS + synchronized" 分段锁思想,
    读操作几乎无锁,写操作 锁单个桶,并发度 ≈ 桶数;支持原子复合 API。

    创建(3 种)

    // 1. 默认并发级别 16
    ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
    
    // 2. 指定初始容量
    ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(100);
    
    // 3. 通过已有 Map 快速构造
    ConcurrentHashMap<String, Integer> map = 
        new ConcurrentHashMap<>(existingMap);

    基本 CRUD(与 HashMap 类似,但线程安全)

    map.put("apple", 3);          // 增/改
    map.get("apple");             // 查,无锁读
    map.remove("apple");          // 删
    map.containsKey("apple");     // 判断 key
    map.size();                   // 近似值(弱一致)
    map.isEmpty();                // 弱一致
    map.clear();                  // 清空

    原子复合 API(高并发神器)

    // 1. 不存在才 put
    map.putIfAbsent("apple", 0);
    
    // 2. 原子累加
    map.merge("apple", 1, Integer::sum); // apple = 旧值 + 1
    
    // 3. 原子计算并放入
    map.computeIfAbsent("banana", k -> loadFromDB(k));
    
    // 4. 存在则计算
    map.computeIfPresent("apple", (k, v) -> v > 5 ? null : v + 1); // 返回 null 会删除
    
    // 5. 原子替换
    map.replace("apple", 3, 4); // 仅当旧值=3 时才替换为 4

    批量操作(线程安全)

    // 批量 put
    Map<String, Integer> batch = Map.of("k1", 1, "k2", 2);
    map.putAll(batch);
    
    // 原子 merge 批量
    batch.forEach((k, v) -> map.merge(k, v, Integer::sum));

    遍历(弱一致,不会抛 ConcurrentModificationException)

    // 1. for-each
    for (Map.Entry<String, Integer> e : map.entrySet()) {
        System.out.println(e.getKey() + "=" + e.getValue());
    }
    
    // 2. Lambda
    map.forEach((k, v) -> System.out.println(k + "=" + v));
    
    // 3. 高级 forEach 并行(利用多 CPU)
    map.forEach(4, (k, v) -> System.out.println(Thread.currentThread().getName() + " " + k));

    原子读-改-写(例子:计数器)

    // 线程安全计数器一行搞定
    map.merge("requestCount", 1, Integer::sum);

    线程安全说明

    • 读操作无锁(volatile + CAS)

    • 写操作只锁单个桶(synchronized 锁住首节点)

    • 弱一致迭代器:遍历的是某一时刻的快照,允许并发修改,不会抛 ConcurrentModificationException

    性能小贴士

        初始容量适当设置,减少 rehash
        并发度默认 16,一般无需修改
        使用 merge、computeIfAbsent 等原子 API 避免手动 synchronized

    背口诀

    ConcurrentHashMap 读无锁写锁桶,原子 merge 计算快,弱一致迭代不抛异常,高并发首选它”。

    ConcurrentHashMap源码

    ConcurrentHashMap(JDK 8 之后)= “数组 + 链表 + 红黑树 + CAS + synchronized 桶锁”
    目标:读操作几乎无锁,写操作只锁单个桶,并发度 ≈ 桶数,线程安全且比 Hashtable 快一个量级。

    下面按 内存布局 → 无锁读 → 桶锁写 → 扩容 → 计数器 → 并发

    核心字段:sizeCtl 多角色控制并发

    transient volatile Node<K,V>[] table;     // 主桶数组
    private transient volatile int sizeCtl;   // 负值表示正在扩容/-1 表示初始化
    static final int MOVED     = -1;          // ForwardingNode 的 hash
    static final int TREEBIN   = -2;          // 红黑树根节点的 hash
    static final int RESERVED  = -3;          // 临时保留节点的 hash

    sizeCtl 在不同阶段扮演 阈值、扩容戳、初始化锁 三重身份,是并发协调的核心信号量

    无锁读:get 全程不加锁,靠 volatile + CAS 保证可见性

    public V get(Object key) {
        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
        int h = spread(key.hashCode());                // 再哈希
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) {   // volatile 读桶头
            if ((eh = e.hash) == h) {                  // 头节点命中
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                    return e.val;
            }
            else if (eh < 0)                           // 树或 ForwardingNode
                return (p = e.find(h, key)) != null ? p.val : null;
            while ((e = e.next) != null) {             // 链表遍历
                if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    return e.val;
            }
        }
        return null;                                   // 未找到
    }

    tabAt 底层 = Unsafe.getObjectVolatileCPU 级别可见性,无需 synchronized

    桶锁写:put 只锁头节点,粒度 = 1 个桶

    final V putVal(K key, V value, boolean onlyIfAbsent) {
        int hash = spread(key.hashCode());
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();                                    // 懒初始化
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { // 桶为空 → CAS 插入
                if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
                    break;                   // CAS 成功退出
            }
            else if ((fh = f.hash) == MOVED)                          // 发现扩容戳 → 帮忙迁移
                tab = helpTransfer(tab, f);
            else {                                                    // 桶已存在 → 锁头节点
                V oldVal = null;
                synchronized (f) {                                    // ① 只锁一个桶头
                    if (tabAt(tab, i) == f) {                         // 二次校验
                        if (fh >= 0) {                                // 链表情况
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    if (!onlyIfAbsent) e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key, value, null);
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) {              // 红黑树情况
                            Node<K,V> p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value);
                            if (p != null) { oldVal = p.val; if (!onlyIfAbsent) p.val = value; }
                        }
                    }
                }
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD) treeifyBin(tab, i); // ≥8 转树
                    if (oldVal != null) return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);      // ② 原子计数 + 扩容检查
        return null;
    }

    CAS 空桶 → 失败才退化到 synchronized 桶头,绝大多数并发落在 CAS 阶段,锁竞争极少。

    扩容:多线程协同迁移(ForwardingNode 标记)

    final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
        Node<K,V>[] nextTab; int sc;
        if (tab != null && (f instanceof ForwardingNode) &&
            (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
            int rs = resizeStamp(tab.length);               // 扩容戳
            while (nextTab == nextTable && table == tab &&
                   (sc = sizeCtl) < 0) {
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
                    break;
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) { // 线程+1
                    transfer(tab, nextTab);                     // 帮忙搬运
                    break;
                }
            }
            return nextTab;
        }
        return table;
    }

    把桶区间 分段分配给多个线程并行迁移,CPU 核数越多扩容越快

    原子计数:LongAdder 思想,分段累加

    private final CounterCell[] counterCells; // 分片计数器
    
    final void addCount(long x, int check) {
        CounterCell[] as; long b, s;
        if ((as = counterCells) != null ||
            !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
            CounterCell a; long v; int m;
            boolean uncontended = true;
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
                !(uncontended = U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
                fullAddCount(x, uncontended);     // 高并发下扩容计数分片
                return;
            }
        }
        if (check <= 1) return;
        if ((s = sumCount()) >= (long)(sc = sizeCtl) &&
            tab != null) {
            if (sc < 0) { /* 正在扩容,协助 */ }
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                transfer(tab, null);               // 触发扩容
            }
        }
    }

    size() 返回 分片求和弱一致无锁

    并发复合 API:全程桶锁或 CAS 保证原子

    V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)
    V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
    V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
    boolean replace(K key, V oldValue, V newValue)

    实现逻辑与 putVal 相同,同一桶锁内完成 判断+计算+更新不会被打断

    设计思想一句话

    读用 volatile+CAS 无锁,写只锁单个桶;CAS 空桶失败才 synchronized 头节点,链表过长转红黑树;2 倍扩容多线程协同搬运,分片计数保证 size 弱一致;复合 API 同锁原子,并发度≈桶数,读远大于写性能爆表。

    使用场景

    实现 线程安全 顺序 读写性能 典型场景一句话
    HashMap 无序 最快 O(1) 单线程只追求速度
    LinkedHashMap ✅插入/访问序 略慢于 HashMap 需要保序(LRU、配置回写、JSON 序列化)
    ConcurrentHashMap ✅高并发 无序 读≈无锁,写锁桶 多线程共享且读远大于写

    HashMap —— “默认选项”

    单线程、顺序无所谓、要极致速度:缓存、计数器、算法临时表。

    Map<ID, User> cache = new HashMap<>(1_000_000);   // 足够快

    LinkedHashMap —— “保序神器”

    插入顺序:导出/序列化保持用户原始顺序

    Map<String, Object> jsonMap = new LinkedHashMap<>(); // 转 JSON 不乱

    访问顺序(LRU 缓存基础)

    Map<Key, Data> lru = new LinkedHashMap<Key, Data>(16, 0.75f, true) {
        protected boolean removeEldestEntry(Map.Entry<Key, Data> eldest) {
            return size() > MAX;          // 超过容量自动淘汰最老
        }
    };

    ConcurrentHashMap —— “并发基石”

    多线程共享且读远大于写:计数器、本地缓存、开关映射。

    private final ConcurrentHashMap<String, LongAdder> counter =
            new ConcurrentHashMap<>();
    
    // 线程安全累加一行搞定
    counter.computeIfAbsent(key, k -> new LongAdder()).increment();

    原子复合 API 替代 synchronized

    map.merge(k, 1, Integer::sum);          // 无锁自增
    map.computeIfAbsent(k, this::loadFromDB); // 懒加载

    决策树(30 秒拍板)

    单线程?
    ├─ 需要保序 → LinkedHashMap
    └─ 无序     → HashMap
    
    多线程?
    └─ ConcurrentHashMap(直接选,别用 Collections.synchronizedMap)

    速记口诀
    单线程无序 HashMap,保序 LinkedHashMap;多线程无脑 ConcurrentHashMap”。

    更多推荐