HashMap HashSet

1. 一眼区分

  • HashMap<K, V>:存的是 key -> value
  • HashSet<E>:存的是单个元素 E
  • Mapput
  • Setadd,没有 put

核心区别

  • HashMapkey 不能重复,value 可以重复
  • HashSet:元素不能重复
  • 两者都属于哈希结构,查找平均时间复杂度通常是 O(1)

2. 常用 API

HashMap 常用方法

  • put(key, value):添加或更新
  • get(key):按 key 取值
  • getOrDefault(key, defaultValue):取值,取不到就返回默认值
  • containsKey(key):判断 key 是否存在
  • containsValue(value):判断 value 是否存在,能用但很少用
  • remove(key):删除指定 key

HashSet 常用方法

  • add(e):添加元素
  • contains(o):判断元素是否存在
  • remove(o):删除元素

Note

Set 只有 contains(Object o),没有 containsKey() 这种说法。

3. 高频写法

计数

map.put(x, map.getOrDefault(x, 0) + 1);

这句是刷题里最常见的 HashMap 用法之一:

  • 如果 x 已经存在,就在原值基础上 +1
  • 如果 x 不存在,就先用默认值 0

本质上就是一种“尝试更新”的写法,和 Math.max() 的思路很像。

判断是否出现过

if (set.contains(x)) {
    // 出现过
} else {
    set.add(x);
}

遍历 Map

for (Map.Entry<Integer, Integer> entry : freq.entrySet()) {
    int key = entry.getKey();
    int value = entry.getValue();
}

相关题目:2._前k个高频元素

4. 为什么推荐 entrySet()

不推荐:keySet() + get()

for (String key : map.keySet()) {
    String value = map.get(key);
}

问题在于:

  • 先拿到 key
  • 再通过 get(key) 回去找一次 value
  • 写起来可以,但不够直接

推荐:entrySet()

for (Map.Entry<String, String> entry : map.entrySet()) {
    String key = entry.getKey();
    String value = entry.getValue();
}

优点:

  • 一次就拿到一对 key-value
  • 语义更清楚
  • 遍历场景下更常用

5. 怎么理解 Map.Entry

可以把 Map 想成一个装满“键值对包裹”的仓库:

  • Map:大仓库
  • Entry:一个个打包好的键值对
  • entrySet():把这些包裹组成一个集合

所以:

  1. Map.Entry<K, V> 是“一个键值对”的类型
  2. map.entrySet() 是把所有键值对拿出来
  3. entry.getKey() / entry.getValue() 是从当前这一个键值对里取值

记忆方式

  • keySet() = key 的集合
  • entrySet() = entry 的集合

之所以叫 entrySet(),是因为返回值本身就是一个 Set

6. Java 8 之后的简写

map.forEach((key, value) -> {
    System.out.println("键:" + key + ",值:" + value);
});

这种写法本质上还是在遍历键值对,只是把 Entry 的细节隐藏掉了。

7. 刷题时要记住的坑

遍历时不要直接修改 Map

如果一边遍历,一边直接增删 Map,可能会报:

ConcurrentModificationException

这类场景通常要配合 Iterator 处理。

8. 最后速记

  • Map 看的是 key
  • Set 看的是元素本身
  • Map 常用:putgetgetOrDefaultcontainsKey
  • Set 常用:addcontains
  • 遍历 Map 优先写 entrySet()