【JDK】JDK源码分析-Map
Map 接口
Map 是一个接口,它表示一种“键-值(key-value)”映射的对象(Entry),其中键是不重复的(值可以重复),且最多映射到一个值(可以理解为“映射”或者“字典”)。
Map 常用的实现类有 HashMap、TreeMap、ConcurrentHashMap、LinkedHashMap 等,它们的继承结构如下:
Map 的方法列表如下:
一些常用方法:
// 将键-值对存入 Map,若 key 对应的 value 已存在,则将其替换
// 返回原先 key 对应的 value(若不存在,返回 null)
V put(K key, V value); // 将指定 Map 中的所有元素拷贝到本 Map 中
void putAll(Map<? extends K, ? extends V> m); // 返回本 Map 中所有 key 的 Set 视图
Set<K> keySet(); // 返回本 Map 中所有 value 的 Collection 视图
Collection<V> values(); // 返回本 Map 中所有 Entry 的 Set 视图
// 其中 Entry 是 Map 内部的一个接口,可以理解为 Map 的“元数据”
Set<Map.Entry<K, V>> entrySet();
此外,JDK 1.8 又增加了不少方法,如下:
// 获取 key 对应的 value,若 value 为 null,则返回 defaultValue
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
} // 遍历 Map 中的元素
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
} // 通过给定的函数计算出新的 Entry 替换所有旧的 Entry
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Objects.requireNonNull(function);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
// ise thrown from function is not a cme.
v = function.apply(k, v);
try {
entry.setValue(v);
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
}
} // 若 key 对应的 value 不存在,则把 key-value 存入 Map,否则无操作
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
} return v;
} // 若 key 对应的值等于 value,则移除 key;否则无操作
default boolean remove(Object key, Object value) {
Object curValue = get(key);
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
remove(key);
return true;
} // 若 key 对应的值等于 oldValue,则将其替换为 newValue;否则无操作
default boolean replace(K key, V oldValue, V newValue) {
Object curValue = get(key);
if (!Objects.equals(curValue, oldValue) ||
(curValue == null && !containsKey(key))) {
return false;
}
put(key, newValue);
return true;
} // Map 中存在 key 时,将 key-value 存入,相当于:
/*
if (map.containsKey(key)) {
return map.put(key, value);
} else
return null;
}
*/
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
} // 当 key 对应的 value 不存在时,使用给定的函数计算得出 newValue
// 并将 key-newValue 存入 Map
default V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}
return v;
} // 当 key 对应的 value 存在时,使用给定的函数计算得出 newValue,
// 当 newValue 不为 null 时将 key-newValue 存入 Map;否则移除 key
default V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue;
if ((oldValue = get(key)) != null) {
V newValue = remappingFunction.apply(key, oldValue);
if (newValue != null) {
put(key, newValue);
return newValue;
} else {
remove(key);
return null;
}
} else {
return null;
}
} // 根据 key 和其对应的 oldValue,使用给定的函数计算出 newValue
// 若 newValue 为 null
// 若 oldValue 不为空或 key 存在,则删除 key-oldValue
// 否则无操作
// 若 newValue 不为 null,用 newValue 替换 oldValue
default V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue = get(key);
V newValue = remappingFunction.apply(key, oldValue);
if (newValue == null) {
// delete mapping
if (oldValue != null || containsKey(key)) {
// something to remove
remove(key);
return null;
} else {
// nothing to do. Leave things as they were.
return null;
}
} else {
// add or replace old mapping
put(key, newValue);
return newValue;
}
} default V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
V oldValue = get(key);
V newValue = (oldValue == null) ? value :
remappingFunction.apply(oldValue, value);
if(newValue == null) {
remove(key);
} else {
put(key, newValue);
}
return newValue;
}
PS: 1.8 中的几个方法看似比较复杂,但有些方法实质上相当于对一些 if...else 语句的封装,利用 lambda 表达式可以让代码更简洁。
Entry 接口
Map 接口内部还定义了一个 Entry 接口(上面已经出现),它其实相当于 Map 内部存储的「元数据」,也就是 键-值(key-value) 映射。方法列表如下:
其中前面几个方法都比较简单,这里分析下后面几个 JDK 1.8 引入的方法,如下:
// 返回一个比较器,它以自然顺序比较 Entry 的 key
public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
} // 返回一个比较器,它以自然顺序比较 Entry 的 value
public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getValue().compareTo(c2.getValue());
} // 返回一个比较器,它使用给定的 Comparator 比较 Entry 的 key
public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
} // 返回一个比较器,它使用给定的 Comparator 比较 Entry 的 value
public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
}
小结
1. Map 接口虽然没有继承自 Collection 接口,但也是 JCF(Java Collections Framework) 的一部分;
2. Map 存储的是键-值(key-value)映射结构的对象;
3. Entry 接口定义在其内部,它是真正定义键-值映射的结构,相当于 Map 的「元数据」。
Stay hungry, stay foolish.
PS: 本文首发于微信公众号【WriteOnRead】。
【JDK】JDK源码分析-Map的更多相关文章
- JDK Collection 源码分析(2)—— List
JDK List源码分析 List接口定义了有序集合(序列).在Collection的基础上,增加了可以通过下标索引访问,以及线性查找等功能. 整体类结构 1.AbstractList 该类作为L ...
- JDK AtomicInteger 源码分析
@(JDK)[AtomicInteger] JDK AtomicInteger 源码分析 Unsafe 实例化 Unsafe在创建实例的时候,不能仅仅通过new Unsafe()或者Unsafe.ge ...
- 设计模式(十八)——观察者模式(JDK Observable源码分析)
1 天气预报项目需求,具体要求如下: 1) 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方). 2) 需要设计开放型 API,便于其他第三方也能接入气象 ...
- Java容器 | 基于源码分析Map集合体系
一.容器之Map集合 集合体系的源码中,Map中的HashMap的设计堪称最经典,涉及数据结构.编程思想.哈希计算等等,在日常开发中对于一些源码的思想进行参考借鉴还是很有必要的. 基础:元素增查删.容 ...
- JDK Collection 源码分析(3)—— Queue
@(JDK)[Queue] JDK Queue Queue:队列接口,对于数据的存取,提供了两种方式,一种失败会抛出异常,另一种则返回null或者false. 抛出异常的接口:add,remove ...
- JDK Collection 源码分析(1)—— Collection
JDK Collection JDK Collection作为一个最顶层的接口(root interface),JDK并不提供该接口的直接实现,而是通过更加具体的子接口(sub interface ...
- Java中集合框架,Collection接口、Set接口、List接口、Map接口,已经常用的它们的实现类,简单的JDK源码分析底层实现
(一)集合框架: Java语言的设计者对常用的数据结构和算法做了一些规范(接口)和实现(实现接口的类).所有抽象出来的数据结构和操作(算法)统称为集合框架. 程序员在具体应用的时候,不必考虑数据结构和 ...
- JDK源码分析(6)ConcurrentHashMap
JDK版本 ConcurrentHashMap源码分析 table:默认为null,初始化发生在第一次插入操作,默认大小为16的数组,用来存储Node节点数据,扩容时大小总是2的幂次方. nextTa ...
- JDK(五)JDK1.8源码分析【集合】HashMap
本文转载自无始无终,原文连接 HashMap 在 JDK 1.8 后新增的红黑树结构 传统 HashMap 的缺点 JDK 1.8 以前 HashMap 的实现是 数组+链表,即使哈希函数取得再好,也 ...
随机推荐
- .NET CORE与Spring Boot编写控制台程序应有的优雅姿势
本文分别说明.NET CORE与Spring Boot 编写控制台程序应有的“正确”方法,以便.NET程序员.JAVA程序员可以相互学习与加深了解,注意本文只介绍用法,不会刻意强调哪种语言或哪种框架写 ...
- Java类库的源码
Java类库中的类,包括System.String.Scanner.Math.Random等:这些类也是用Java编写的. Java类库中包含数千个文件,其中的很多文件都包含数千行代码:因为Java类 ...
- Spring Boot2(七):拦截器和过滤器
一.前言 过滤器和拦截器两者都具有AOP的切面思想,关于aop切面,可以看上一篇文章.过滤器filter和拦截器interceptor都属于面向切面编程的具体实现. 二.过滤器 过滤器工作原理 从上图 ...
- SqlHelper(基础)
using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; usin ...
- 微服务-springboot-activiti工作流
idea中安装aciviti并使用,链接地址:https://blog.csdn.net/qq_41728540/article/details/79506463 一.创建springboot项目,勾 ...
- SQL参数化查询
参数化查询(Parameterized Query 或 Parameterized Statement)是指在设计与数据库链接并访问数据时,在需要填入数值或数据的地方,使用参数 (Parameter) ...
- select自定义下拉三角符号,css样式小细节
本来没有写文章的习惯,但是闲下来了,整理资料,发现还挺纠结,对前端来说.所以整理下,希望对看到的人有所帮助,毕竟我不是前端开发. 起因,是前端告诉我select 框的三角箭头不能自定义.但是第二次的时 ...
- 学Redis这篇就够了
Redis 简介 Redis 优势 Redis 数据类型 string hash list set Zset 小总结 基本命令 发布订阅 简介 实例 发布订阅常用命令 事务 实例 Redis 事务命令 ...
- linux学习书籍推荐《鸟哥的Linux私房菜》下载
下载地址:点我 <鸟哥的Linux私房菜:基础学习篇>是具有知名度的Linux入门书<鸟哥的Linux私房菜基础学习篇>的最新版,全面而详细地介绍了Linux操作系统.< ...
- (转)代码结构中Dao,Service,Controller,Util,Model是什么意思?
作者:技能树IT修真院链接:https://www.zhihu.com/question/58410621/answer/623496434来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商 ...