一、Map 集合体系
Map 是 Map 集合体系的根接口
a) Map 集合特点
b) 常用方法
- 如果集合中不存在该 key:直接新增,返回null
- 如果集合已经存在该 key:新 value 覆盖旧 value,返回被覆盖的旧 value
⭐开发重点方法
常用方法完整代码示例
import java.util.HashMap;
import java.util.Map;
public class MapDemo {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
// 1.put 添加键值对
map.put("张三", 18);
map.put("李四", 20);
Integer oldValue = map.put("张三", 25); // key已存在,覆盖旧值,返回18
System.out.println("被覆盖的旧值:" + oldValue);
// 2.size 获取键值对个数
System.out.println("集合大小:" + map.size());
// 3.get 根据key获取value
System.out.println("张三的年龄:" + map.get("张三"));
// 4.containsKey 判断是否包含key
System.out.println("是否包含王五:" + map.containsKey("王五"));
// 5.containsValue 判断是否包含value
System.out.println("是否包含年龄25:" + map.containsValue(25));
// 6.remove 根据key删除
Integer removed = map.remove("李四");
System.out.println("被删除的值:" + removed);
}
}
c) Map 实现类
如果自定义类对象作为 HashMap 的键,为保证 key 不重复,对应类必须重写hashCode()和equals()。 开发中一般优先使用String、Integer作为 key(用户名、id 等),检索效率更高。
底层结构:JDK1.8 之前 = 数组 + 链表;JDK1.8 及之后 = 数组 + 链表 + 红黑树(链表长度超过 8 且数组长度≥64 时转红黑树)
📝面试题:写出 HashMap 和 Hashtable 的区别
表格
| 出现版本 | JDK1.2 | JDK1.0 |
| 线程安全 | 不安全,效率高 | 安全,方法加 synchronized,效率低 |
| null 键 /null 值 | 都允许 | 都不允许,抛 NullPointerException |
| 父类 | AbstractMap | Dictionary |
| 迭代器 | Iterator(fail-fast 快速失败) | Enumerator |
| 默认初始容量 | 16 | 11 |
| 扩容机制 | 2 倍 | 2 倍 + 1 |
import java.util.Properties;
import java.io.FileInputStream;
public class PropertiesDemo {
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
// 加载配置文件
prop.load(new FileInputStream("config.properties"));
// 根据key获取value
String username = prop.getProperty("username");
System.out.println(username);
}
}
d) Map 三种遍历方式
Map 本身没有索引,不能直接 for 循环遍历,提供三种遍历方案
① 键遍历 keySet ()
获取全部 key 组成 Set 集合,再通过 key 调用 get () 拿到 value
Set<K> ks = map.keySet();
for(K key : ks){
V value = map.get(key);
System.out.println(key + " = " + value);
}
② 值遍历 values ()
只获取集合中所有 value,拿不到 key
Collection<V> vs = map.values();
for(V value : vs){
System.out.println(value);
}
注意:Map 没有通过 value 反向获取 key 的方法
③ 键值对遍历 entrySet ()(效率较高,推荐)
把每一组key‑value封装为Map.Entry对象
Set<Map.Entry<K,V>> kvs = map.entrySet();
for(Map.Entry<K,V> kv : kvs){
K key = kv.getKey();
V value = kv.getValue();
System.out.println(key + " = " + value);
}
三种遍历完整可运行代码
import java.util.*;
public class MapIterator {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("Java", 100);
map.put("Python", 95);
map.put("C++", 90);
// 方式1:keySet 键遍历
System.out.println("===== keySet遍历 =====");
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println(key + " -> " + map.get(key));
}
// 方式2:values 值遍历
System.out.println("===== values遍历 =====");
Collection<Integer> values = map.values();
for (Integer value : values) {
System.out.println(value);
}
// 方式3:entrySet 键值对遍历(推荐)
System.out.println("===== entrySet遍历 =====");
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
}
}
二、HashMap 高频面试题补充
1. HashMap 底层原理
- JDK1.7:数组 + 链表,头插法
- JDK1.8:数组 + 链表 + 红黑树,尾插法
- 链表转红黑树条件:链表长度 ≥ 8 且 数组长度 ≥ 64
- 红黑树退化为链表条件:节点数 ≤ 6
2. HashMap 扩容机制
- 默认初始容量:16
- 负载因子:0.75
- 扩容阈值 = 容量 × 负载因子 = 16 × 0.75 = 12
- 元素个数达到阈值时,扩容为原来的2 倍
- 扩容时会重新计算所有元素的 hash 位置(rehash)
3. HashMap 为什么线程不安全?
- 多线程同时 put 可能导致数据覆盖
- JDK1.7 扩容时头插法可能导致链表成环,死循环
- 解决方案:ConcurrentHashMap(推荐)、Hashtable、Collections.synchronizedMap()
4. 自定义对象作为 key 注意事项
必须同时重写hashCode()和equals():
- hashCode 相同:对象不一定相同,需要再用 equals 判断
- equals 相同:hashCode 一定相同
- 只重写 equals 不重写 hashCode:两个逻辑相等的对象会被存到不同位置,无法去重
class Student {
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
网硕互联帮助中心



评论前必须登录!
注册