前面我们学完了 Collection单列集合:List、Set。

今天正式进入Java集合最重要、面试问得最多、工作用得最频繁的集合:Map双列集合

如果说 List 是一排柜子、Set 是独立储物柜; 那 Map 就是身份证一个编号(Key)对应一个人(Value)

Map 全部以 键值对(Key-Value) 方式存储数据,这是和之前所有集合最大的区别。

本篇重点讲解 HashMap(重中之重),通俗拆解底层哈希表、哈希冲突、JDK1.7与JDK1.8区别、扩容机制、红黑树转换、遍历方式。全篇没有难懂源码,新手一次性看懂。

本篇核心学习目标:

  • 理解Map集合特点、键值对存储结构

  • 精通HashMap底层结构、存储流程

  • 弄懂哈希冲突、怎么解决冲突

  • 吃透JDK1.8优化(链表转红黑树)

  • 掌握HashMap四种遍历方式

  • 分清HashMap、TreeMap、HashTable区别

  • 背熟高频Map面试题


一、Map集合是什么?能干什么?

1.1 Map存储结构

Map = 键(Key) + 值(Value)

生活举例:

  • 手机号(Key) = 联系人姓名(Value)

  • 学号(Key) = 学生信息(Value)

  • IP地址(Key) = 地理位置(Value)

1.2 Map核心特点(必背)

  1. 键(Key)唯一、不可重复

  2. 值(Value)可重复

  3. 一个键只能对应一个值

  4. 无序、无索引

  5. 键重复会覆盖旧值

1.3 Map常用实现类

实现类

底层结构

顺序

线程安全

使用场景

HashMap

哈希表

无序

不安全

日常开发首选

TreeMap

红黑树

按键排序

不安全

需要排序

HashTable

哈希表

无序

安全

老旧、已淘汰


二、Map常用方法(工作天天写)

2.1 通用基础方法

put(key,value)        // 添加/覆盖元素
get(key)               // 根据键取值
remove(key)            // 根据键删除
containsKey(key)       // 判断键是否存在
containsValue(value)   // 判断值是否存在
size()                 // 获取键值对个数
clear()                // 清空集合
keySet()               // 获取所有键
values()               // 获取所有值
entrySet()             // 获取键值对对象

2.2 代码基础演示

import java.util.HashMap;
import java.util.Map;

public class MapDemo {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();

        // 添加数据
        map.put("001","张三");
        map.put("002","李四");
        map.put("001","张三三"); // 键重复,覆盖旧值

        // 查询
        System.out.println(map.get("001")); // 输出:张三三

        // 删除
        map.remove("002");

        System.out.println(map);
    }
}

重点:键重复不会报错,直接覆盖旧数据!


三、HashMap底层原理(面试必考核心)

3.1 底层结构演变

JDK1.7:数组 + 单向链表
JDK1.8:数组 + 单向链表 + 红黑树(重点)

3.2 初始化参数(必须记住)

  • 默认初始容量:16

  • 加载因子:0.75

  • 扩容阈值:16 * 0.75 = 12

  • 链表转红黑树条件:链表长度>8 && 数组长度>=64

  • 红黑树退化成链表:节点小于6

3.3 HashMap存储流程(通俗大白话)

  1. 首次put,创建长度为16的数组

  2. 通过key计算hash值,定位数组下标

  3. 下标位置为空:直接存入

  4. 下标位置有数据:发生哈希冲突

  5. 冲突后:挂在链表尾部(JDK1.8尾插法)

  6. 链表过长:长度超过8,数组达标64,转为红黑树

  7. 达到阈值12,自动扩容为原来2倍

3.4 什么是哈希冲突?

不同的Key,经过哈希算法算出相同数组下标,就叫哈希冲突。

HashMap三种解决冲突方式:
  1. 哈希算法(尽量分散)

  2. 链表法(冲突后挂链表)

  3. 红黑树(超长链表优化查询)

3.5 JDK1.8重大优化(面试高频)

  • 头插法改为尾插法(避免循环链表死链)

  • 新增红黑树,查询速度大幅提升

  • 优化hash算法,减少冲突概率


四、HashMap四种遍历方式(工作必写)

4.1 方式一:遍历键(keySet)

for (String key : map.keySet()) {
    System.out.println(key + "=" + map.get(key));
}

4.2 方式二:遍历值(values)

for (String value : map.values()) {
    System.out.println(value);
}

4.3 方式三:键值对遍历(entrySet,推荐最快)

for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey()+"="+entry.getValue());
}

4.4 方式四:Lambda遍历(简洁)

map.forEach((k,v)-> System.out.println(k+"="+v));

开发最优遍历:entrySet 效率最高,没有重复查询。


五、HashMap、HashTable、ConcurrentHashMap区别

这是面试必问三连,直接背下表:

集合

线程安全

效率

key、value是否允许null

底层

HashMap

不安全

最快

key最多一个null,value任意null

哈希表

HashTable

安全

都不允许null

哈希表

ConcurrentHashMap

安全

较快

不允许null

分段锁哈希表

口诀:单线程HashMap,多线程ConcurrentHashMap,HashTable彻底不用。


六、新手高频易错坑(避坑指南)

  1. HashMap键重复直接覆盖,不会报错

  2. HashMap允许一个key为null,多个value为null

  3. JDK1.8链表是尾插法,不是头插

  4. 链表转红黑树必须同时满足两个条件,不是到8就转

  5. HashMap线程不安全:多线程put可能数据丢失

  6. 不要在循环中频繁keySet遍历取值,效率低,优先entrySet


七、本篇总结

本篇是集合框架最难、最重要的一篇,你必须牢牢记住:

  • Map是双列集合,键唯一、值重复、无序无索引

  • HashMap底层:数组+链表+红黑树

  • 默认容量16、加载因子0.75、阈值12

  • 哈希冲突:链表挂载,过长转红黑树

  • 四种遍历,优先entrySet

  • 单线程HashMap,多线程ConcurrentHashMap

更多推荐