JavaSE 集合 Vol.2 Map 源码入门
1. 前言
· 往期传送门:JavaSE 集合 Vol.1 Collection 源码入门 (含截图分析)
· 本文章是用于 个人学习过程中 整理知识点的帖子,主题为:JavaSE 集合 Vol.2 Map 源码入门
· 本文章 只包含了各个 集合实现类 常用的方法源码分析,并不全面,旨在培养源码的阅读能力。
· 本文章出现的 遗漏、错误 欢迎点开这篇文章的各位指出。
· 本文章的知识大纲根据 韩顺平老师 JavaSE 教学视频 进行编写。
【感谢韩顺平老师带来的优质教学和对教育作出的贡献】
2. Map 集合 框架体系图
3. Map 集合
● 基本介绍 [ JDK1.8版本后 ] ●
· Map 与 Collection 接口 属于 并列关系 。
· Map 用于保存具有映射关系的数据:Key-Value
· Key 和 Value 两个参数可以是 任何引用类型 的数据,它们会被封装成一个结点 HashMap$Node
● 基本特点 [ JDK1.8版本后 ] ●
· Map 中的 Key 值 不允许重复,但 Value 值 可以 。
【给重复 Key 值赋值时,会覆盖原先的 Value 值 】
· Map 中的 Key 值 可以为空,但只能有一个 ,Value 值 可以有多个空值 。
· 通常都会使用 String 类型 作为 Key 值 。
· Key 和 Value 存在 单向一对一关系,通过指定的 Key 值 可以找到对应的 Value 值。
· Map 遍历元素 和 插入元素 的 顺序不一致 。
● 常用方法 ●
指令 | 解释 |
---|---|
put ( Object key . Object value ) | 添加 单个 键值对 元素 |
get ( Object key ) | 根据 键 获取 单个元素 对应的值 |
remove ( Object key ) | 根据 键 删除 单个元素 |
remove ( Object key . Object value ) | 根据具体的 键值对 删除 单个元素 |
size ( ) | 获取当前 Map 对象的长度 |
isEmpty ( ) | 查看当前 Map 对象是否为空 |
clear ( ) | 清空当前 Map 对象 |
containsKey ( Object key ) | 查看当前 Map 对象是否有指定 键 |
● HashMap 实现类
● 结点类型 [ 详解 ] ●
在 JavaSE 集合 Vol.1 Collection 源码入门 中 讲解 LinkedHashSet 时,引入了 结点类型 的概念 。
其实 结点类型 在 HashSet 中就已经体现出来,但当时没有具体说明。
而 HashSet 又跟 HashMap 有关,所以放在这里讲解。
下面将着重讲解 HashMap 中 的结点类型:
① HashMap 通过键值对的形式存放数据 将这些数据封装成 HashMap$Node
内部类
这个 内部类 也就是我们常说的 结点 【 这些结点被存放在 HashMap 中 】
● 添加元素时,调用的 newNode 方法 返回是
HashMap$Node
内部类 的 有参构造对象
② 这些 结点 实现了 Map 的 Entry 接口
● 此时可以得出结论
HashMap$Node
是Entry
的实现类
③ 为了方便管理和遍历这些结点,HashMap 又准备了一个 EntrySet 集合 :HashMap$EntrySet
这个集合其实是 HashMap 的内部类,里面存放的是 Entry 类型 的结点
我们知道 HashMap$Node
是 Entry 的实现类,这里的 Entry 其实就是 HashMap$Node
内部类。
● 创建 EntrySet 集合
● 每一个 Entry 指向的就是 对应的HashMap$Node
本身
④ 关于遍历结点,核心方法是 Entry 接口 中的 getKey() 和 getValue() 方法
我们可以通过这两个方法分别获取每个对应结点的 键和值 。
也可以直接使用 HashMap 的 KeySet() 和 Values() 方法 单独获取 键集合 和 值集合 。
● 获取每个结点的键和值
● 获取 当前 HashMap 对象的 键集合 和 值集合
● 结点类型 [ 总结 ] ●
· HashMap 底层维护的是其本身,结构为【数组+链表+红黑树】【JDK 8 版本后】
· 每次添加的元素都是以 键值对 的形式添加进来的,HashMap 会将添加的元素封装成 结点 。
· 这些 结点 会被存放进 HashMap 的 Table 数组 中 。
· 为了方便使用和遍历这些 结点,HashMap 开辟了一个 EntrySet 集合 来供 开发者使用 。
· EntrySet 集合 本身并非实际空间,它只是一个引用地址,指向的依旧是 结点 本身。
● 修改元素数据 ●
● 修改元素数据 ●
● HashTable 实现类
● 基本介绍 ●
· HashTable 与 HashMap 的相同点:
① 添加的元素都是以 键值对 形式存放的 。
② 使用方法基本上一致 。
· HashTable 与 HashMap 的不同点:
① HashTable 的 键值对 都不能为 空值,否则会抛出 NullPointerException
异常 。
② HashTable 是 线程安全的 ,HashMap 是 线程不安全的 。
● 扩容机制 ●
HashTable 底层维护的是 HashTable$Entry[]
数组
每次添加数据时 都会调用 addEntry 方法 添加相应的键值对,封装成 Entry 结点
首次添加数据时,HashTable 就会让 这个数组初始化为11 。阈值 = 11× 0.75 = 8
当 Entry数组的长度 不小于 阈值 时,就进行扩容机制。
综上所述:下面将对 扩容机制 进行源码分析
● 扩容机制 ●
● Properties 实现类
● 基本介绍 ●
· Properties 实现类 继承自 HashTable 实现类,是 HashTable 的子类,同时它也实现了 Map 接口。
· Properties 存放数据的方式也是 键值对 形式。它的使用特点和 HashTable 一致。
· Properties 最广泛的使用是:从 xxx.properties 文件中,加载数据到 Properties 类对象,并进行数据
的读取和修改 。
● 基本使用 ●
· 由于 Properties 类的使用涉及到 IO 流 的知识,所以我们将在 IO 流 专题进行讲解 。
· 传送门 : JavaSE IO流 Vol.1 基础 入门
● TreeMap 实现类
● 自定义排序 [ 详解 ] ●
· 在 JavaSE 集合 Vol.1 Collection 源码入门 中 讲解 TreeSet 时,介绍了 自定义排序 的过程 。
· 因 TreeSet 跟 TreeMap 有关,所以放在这里讲解。
下面将着重讲解 TreeMap 中 的 自定义排序 源码过程:
① 先对 TreeMap 进行 有参构造 源码分析:
● 有参构造 ●
② 掌握 有参构造 后,接下来进行 添加数据 的源码分析:
● 添加数据 ●