Map 是 Java 集合框架中键值对(Key-Value Pair)的存储容器,核心特征是键的唯一性——一个键只能映射到一个值,新值会覆盖同键的旧值,它与 Collection 接口是并列的顶级集合类型,不继承 Collection。
一、核心特性
1. 键的唯一性:键(Key)必须重写 hashCode() 和 equals() 方法来保证唯一性,值(Value)可以重复。
2. 无序/有序特性:不同实现类对元素的存储顺序不同,如 HashMap 无序,TreeMap 按键排序,LinkedHashMap 按插入顺序保存。
3. 允许 null 值:HashMap、LinkedHashMap 允许键和值为 null(键只能有一个 null),TreeMap 不允许键为 null,Hashtable 则键和值都不允许 null。
4. 线程安全:Hashtable 是线程安全的(方法加了 synchronized ),HashMap 是线程不安全的;并发场景下推荐使用 ConcurrentHashMap ,它的并发性能优于 Hashtable。
二、核心接口方法
Map 接口定义了操作键值对的核心方法,常用的有:
- V put(K key, V value) :将键值对存入 Map,若键已存在则覆盖旧值并返回旧值,否则返回 null。
- V get(Object key) :根据键获取对应的值,键不存在则返回 null。
- V remove(Object key) :删除指定键的键值对,返回被删除的值。
- boolean containsKey(Object key) :判断 Map 中是否包含指定键。
- boolean containsValue(Object value) :判断 Map 中是否包含指定值。
- Set<K> keySet() :返回 Map 中所有键的 Set 集合。
- Collection<V> values() :返回 Map 中所有值的 Collection 集合。
- Set<Map.Entry<K,V>> entrySet() :返回 Map 中所有键值对( Map.Entry 对象)的 Set 集合,是遍历 Map 的高效方式。
- void clear() :清空 Map 中的所有键值对。
- int size() :返回 Map 中键值对的数量。
三、Map.Entry 内部接口
Map.Entry<K,V> 是 Map 的静态内部接口,用于表示一个键值对对象,提供了操作单个键值对的方法:
- K getKey() :获取当前键值对的键。
- V getValue() :获取当前键值对的值。
- V setValue(V value) :修改当前键值对的值,返回旧值。
四、常见实现类
1. HashMap
- 底层实现:基于哈希表(数组+链表/红黑树),JDK 1.8 后当链表长度超过阈值(默认 8)且数组长度≥64 时,链表转为红黑树以提升查询效率。
- 特点:无序存储,查询、插入、删除效率高(平均时间复杂度 O(1)),线程不安全,允许键和值为 null。
- 适用场景:非并发场景下的常规键值对存储,是最常用的 Map 实现类。
2. LinkedHashMap
- 底层实现:基于哈希表+双向链表,继承自 HashMap。
- 特点:按插入顺序或访问顺序保存键值对,查询效率略低于 HashMap,线程不安全,允许键和值为 null。
- 适用场景:需要保持键值对插入顺序或访问顺序的场景(如 LRU 缓存的基础实现)。
3. TreeMap
- 底层实现:基于红黑树。
- 特点:按键的自然顺序或**自定义比较器(Comparator)**排序,查询、插入、删除时间复杂度 O(log n),线程不安全,键不允许为 null。
- 适用场景:需要对键进行排序的场景。
4. Hashtable
- 底层实现:基于哈希表,是 JDK 早期的 Map 实现类。
- 特点:线程安全(方法加锁),但并发性能低,键和值都不允许为 null,无序存储。
- 适用场景:已被 ConcurrentHashMap 替代,仅用于兼容旧代码。
5. ConcurrentHashMap
- 底层实现:JDK 1.7 基于分段锁,JDK 1.8 基于哈希表+红黑树+CAS 机制,取消了分段锁。
- 特点:线程安全,并发性能高(支持多线程同时操作不同的桶),键和值都不允许为 null。
- 适用场景:高并发环境下的键值对存储。
五、遍历方式
Map 有三种常见的遍历方式,效率从高到低排序为: entrySet() > keySet() > values() (仅遍历值)
1. 通过 entrySet 遍历(推荐,高效)
直接获取所有键值对对象,一次遍历即可获取键和值。
2. 通过 keySet 遍历
先获取所有键的集合,再通过键获取对应的值,会额外触发 get() 方法的哈希查询。
3. 通过 values 遍历
仅遍历所有值,无法获取对应的键。
六、使用注意事项
1. 键的 hashCode() 和 equals() 重写
自定义对象作为键时,必须重写这两个方法,否则会导致键的唯一性判断失效(如 HashMap 中出现重复键)。
2. 线程安全问题
多线程环境下,避免使用 HashMap,优先选择 ConcurrentHashMap ;不要在遍历 Map 时进行增删操作,否则会抛出 ConcurrentModificationException ,可通过迭代器的 remove() 方法或使用并发集合解决。
3. 初始容量和负载因子
HashMap 的默认初始容量是 16,负载因子是 0.75;当元素数量达到 容量×负载因子 时,会触发扩容(重新哈希),频繁扩容会影响性能,可根据预期存储数量设置合适的初始容量。
942

被折叠的 条评论
为什么被折叠?



