Map接口不存储数据,仅定义键值对操作契约;具体结构由实现类决定:HashMap为数组+链表/红黑树,TreeMap为红黑树,均非线程安全。

Map 接口本身不存储数据,只是定义键值对操作契约
Java 中的 Map 是一个接口,不是具体实现类,所以它没有“结构”可言——它不维护任何底层数据(比如数组、红黑树或链表)。它只规定了行为:如何存 put(K key, V value)、取 get(Object key)、判断存在 containsKey(Object key)、遍历 entrySet() 等。真正有结构的是它的实现类,比如 HashMap(数组 + 链表/红黑树)、TreeMap(红黑树)、LinkedHashMap(哈希表 + 双向链表)。
HashMap 的实际结构:数组 + 拉链 + 红黑树升级
日常用得最多的是 HashMap,它的底层是:一个 NodeNode(单个键值对)、一个链表(多个哈希冲突的 Node),或者当链表长度 ≥ 8 且数组长度 ≥ 64 时,自动转为红黑树(TreeNode)。
- 哈希值通过
key.hashCode()计算,再与数组长度减一做位运算(&)确定下标 -
null键被特殊处理,固定放在数组索引 0 的位置(仅限HashMap,ConcurrentHashMap不允许null键) - 扩容触发条件是
size > capacity * loadFactor(默认 0.75),扩容后容量翻倍,所有元素 rehash
TreeMap 的结构依赖比较逻辑,不是哈希
TreeMap 完全不使用哈希,而是基于红黑树实现,要求键类型必须可比较:要么实现 Comparable 接口,要么构造时传入 Comparator。它的“结构”是自平衡二叉查找树,所以 get、put、remove 时间复杂度稳定为 O(log n),且天然支持按 key 排序遍历(firstKey()、tailMap(K fromKey) 等)。
- 不能存
null键(会抛NullPointerException),但可以存null值 - 如果 key 没有自然顺序,又没传
Comparator,运行时调用put就会立即报ClassCastException - 迭代顺序始终是 key 的排序顺序,和插入顺序无关
别把 Map 当线程安全容器用
所有标准 Map 实现(HashMap、TreeMap、LinkedHashMap)都不是线程安全的。多线程写入时,哪怕只是 put,也可能导致死循环(HashMap 扩容时的并发 bug)、数据丢失或 ConcurrentModificationException。
立即学习“Java免费学习笔记(深入)”;
- 需要并发读写,优先选
ConcurrentHashMap(分段锁 or CAS + synchronized 链表头) - 只读场景下,可用
Collections.unmodifiableMap(m)包装,但注意这只是运行时防护,不防内部修改 -
Hashtable虽线程安全,但方法全加synchronized,性能差,已基本被弃用
键值对集合的“结构”差异,本质是不同实现对时间/空间/顺序/并发需求的权衡。选错实现类,轻则性能骤降,重则运行时崩溃——尤其是哈希类与比较类混用、或在并发场景误信 HashMap 的“看起来能跑”。










