Map

ℹ️定位说明

本篇是 Map 系列总览:Map 核心特性 + 四实现类对比 + 选型入门。深读动线:HashMapHashMap 源码LinkedHashMap(LRU)→ TreeMap(排序,含 红黑树 前置篇)。

Map 核心特性

  1. 键值对存储

    • 每个条目由 键(Key)值(Value) 组成,键唯一(底层通过 equals()hashCode() 保证),值可重复。
    • 典型操作:put(K key, V value)(插入 / 更新)、get(Object key)(通过键取值)、containsKey(Object key)(判断键是否存在)。
  2. 无序性与有序性

    • 无序 Map(如 HashMap):键值对的存储顺序与插入顺序无关,遍历时顺序不可预测。
    • 有序 Map(如 LinkedHashMapTreeMap):
      • LinkedHashMap插入顺序访问顺序维护条目顺序。
      • TreeMap键的自然排序自定义比较器排序(如字典序、数值大小)。
  3. 线程安全性

    • 核心实现类(HashMapLinkedHashMapTreeMap非线程安全,多线程场景需使用 ConcurrentHashMap 或手动同步(如 Collections.synchronizedMap())。
  4. null 支持

    • HashMapLinkedHashMap:允许 1 个 null 键多个 null 值TreeMap 不允许null 键,Hashtable 不允许任何 null)。

核心实现类概览

实现类 数据结构 顺序性 线程安全 null 键支持 典型场景
HashMap 哈希表(数组 + 链表 + 红黑树) 无序 非安全 允许 快速键值对查询(如缓存、字典)
LinkedHashMap 哈希表 + 双向链表 插入 / 访问顺序 非安全 允许 有序存储、LRU 缓存
TreeMap 红黑树(自平衡二叉树) 自然 / 定制排序 非安全 不允许 排序、范围查询(如字典序统计)
Hashtable(见总览) 哈希表(数组 + 链表) 无序 安全(同步) 不允许 旧系统线程安全场景(极少使用)

与 Set、Queue 的对比

接口 / 类 存储形式 核心特性 典型场景
Set 单一元素 元素唯一、无序 / 有序(取决于实现类) 去重、唯一性校验
Queue 线性元素(FIFO) 先进先出、支持阻塞操作(如 BlockingQueue 任务队列、消息传递
Map 键值对 快速映射、键唯一、值可重复 数据关联、配置管理、统计计数

快速入门建议

  1. 优先使用 HashMap

    若无特殊顺序或线程安全需求,HashMap 是性能最优的选择,适用于高频的 put/get 操作(如用户信息存储、缓存系统)。

  2. 需要有序性时

    • 按插入顺序:选 LinkedHashMap(如用户操作日志记录)。
    • 按键排序:选 TreeMap(如学生成绩排名、按时间戳排序的事件数据)。
  3. 线程安全场景

    • 高并发:使用 ConcurrentHashMap(JDK 8 优化后性能显著提升)。
    • 低并发且需兼容旧代码:使用 Hashtable(不推荐,仅用于遗留系统)。
  4. 避免误区

    • 不要用 TreeMap 替代 HashMap 做普通映射(排序开销会降低性能)。
    • LinkedHashMap 的访问顺序模式需配合 removeEldestEntry 实现 LRU,否则无法自动淘汰旧元素。

通过理解 Map 的核心特性与实现类差异,可根据业务需求快速选择合适的数据结构,提升代码效率和可维护性。

上手示例:遍历三式与计数套路

Map<String, Integer> score = new HashMap<>();
score.put("Alice", 92);
score.put("Bob", 85);

// 1. entrySet——键值都要,首选(一次遍历拿全)
for (Map.Entry<String, Integer> e : score.entrySet()) {
    System.out.println(e.getKey() + " = " + e.getValue());
}

// 2. keySet——只要键,值用 get 补(两次查找,略慢)
for (String name : score.keySet()) {
    System.out.println(name);
}

// 3. values——只要值
for (Integer s : score.values()) {
    System.out.println(s);
}

// 4. forEach(JDK 8+,写起来最短)
score.forEach((name, s) -> System.out.println(name + ":" + s));
💡两个日常最高频的套路

词频统计——别写 if (map.containsKey(k)) map.put(k, map.get(k)+1) 三行了:

`map.merge(word, 1, Integer::sum);`            // 存在则加 1,不存在放 1
map.putIfAbsent(word, 0);                    // 只在缺键时补默认值
int def = map.getOrDefault("nobody", 0);     // 取值给默认值,避免 null

这三个方法(merge / putIfAbsent / getOrDefault)是 JDK 8 给 Map 的"日常三件套",刷题和业务代码里到处都是。

--- ⬅️ PriorityQueue 🏠 00-Java ➡️ HashMap