Java中HashMap的源码分析
先来回顾一下Map类中常用实现类的区别:
HashMap:底层实现是哈希表+链表,在JDK8中,当链表长度大于8时转换为红黑树,线程不安全,效率高,允许key或value为null
HashTable:底层实现是哈希表+链表,线程安全,效率低,不允许key或value为null(现在不推荐使用)
TreeMap:底层实现是红黑树,即可按照键的大小排序,如果是自定义的类,可以通过实现Comparator接口重写compare方法
为了更好的理解,建议先对数据结构中链表和哈希表有一个认识^ ^
通俗来说哈希表就是通过一个哈希函数将当前元素的关键字映射到数组中的一个存储位置。
即 存储位置 = f(关键字),其中f即为哈希函数。
因为存储空间有线,我们这个函数不可能做到输入任意元素都能得到不同的存储位置,所以当两个不同的元素通过哈希函数得到了相同的存储地址时,就出现了哈希冲突,这里的链表就是为了解决哈希冲突存在的,我们可以将相同地址的元素以链表的形式存储在相应的位置。HashMap就是运用了这种链表+数组的方式。即如下图所示。
然后我们来一点一点分析HashMap的源码。
HashMap的主干是一个Node数组,每一个Node包含一个key-value键值对。
// table即为HashMap的主干数组,该数组长度为2的次幂
transient Node<K,V>[] table;
我们可以再来看一下Node的源码。可以看到每个Node都是由一个hash值、k-v键值对、next引用(指向下一个Node)构成。
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
// 下面的不是很关键,我们主要关注Node的结构
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
了解了HashMap的内部结构,我们就先来看看简单一点的get方法。
从这里可以看到get函数很简单,就是通过获取所需要查询元素的哈希值找到节点,然后返回节点的value,所以其中关键就是getNode函数。
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
Java中HashMap的源码分析的更多相关文章
- 死磕 java集合之ConcurrentHashMap源码分析(三)
本章接着上两章,链接直达: 死磕 java集合之ConcurrentHashMap源码分析(一) 死磕 java集合之ConcurrentHashMap源码分析(二) 删除元素 删除元素跟添加元素一样 ...
- java线程池ThreadPoolExector源码分析
java线程池ThreadPoolExector源码分析 今天研究了下ThreadPoolExector源码,大致上总结了以下几点跟大家分享下: 一.ThreadPoolExector几个主要变量 先 ...
- 死磕 java集合之DelayQueue源码分析
问题 (1)DelayQueue是阻塞队列吗? (2)DelayQueue的实现方式? (3)DelayQueue主要用于什么场景? 简介 DelayQueue是java并发包下的延时阻塞队列,常用于 ...
- 死磕 java集合之PriorityBlockingQueue源码分析
问题 (1)PriorityBlockingQueue的实现方式? (2)PriorityBlockingQueue是否需要扩容? (3)PriorityBlockingQueue是怎么控制并发安全的 ...
- 死磕 java集合之PriorityQueue源码分析
问题 (1)什么是优先级队列? (2)怎么实现一个优先级队列? (3)PriorityQueue是线程安全的吗? (4)PriorityQueue就有序的吗? 简介 优先级队列,是0个或多个元素的集合 ...
- 死磕 java集合之CopyOnWriteArraySet源码分析——内含巧妙设计
问题 (1)CopyOnWriteArraySet是用Map实现的吗? (2)CopyOnWriteArraySet是有序的吗? (3)CopyOnWriteArraySet是并发安全的吗? (4)C ...
- 死磕 java集合之LinkedHashSet源码分析
问题 (1)LinkedHashSet的底层使用什么存储元素? (2)LinkedHashSet与HashSet有什么不同? (3)LinkedHashSet是有序的吗? (4)LinkedHashS ...
- 死磕 java集合之ArrayDeque源码分析
问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...
- java多线程----线程池源码分析
http://www.cnblogs.com/skywang12345/p/3509954.html 线程池示例 在分析线程池之前,先看一个简单的线程池示例. 1 import java.util.c ...
随机推荐
- short URL 短网址实现原理剖析
short URL 短网址实现原理剖析 意义,简短便于分享,避免出现超长 URL 的字符长度限制问题 原理分析, 使用 HashMap 存储对应的映射关系 (长度不超过7的字符串,由大小写字母加数字共 ...
- github & coding 2018
github & coding 2018 github & coding all in one https://github.com/topics/javascript react r ...
- how to stop MongoDB from the command line
how to stop MongoDB from the command line stop mongod https://docs.mongodb.com/manual/tutorial/manag ...
- 微前端 & 微前端实践 & 微前端教程
微前端 & 微前端实践 & 微前端教程 微前端 micro frontends https://micro-frontends.org/ https://github.com/neul ...
- vue & watch props
vue & watch props bug OK watch: { // props // chatObj: () => { // // bug // log(`this.chatObj ...
- uniapp vue mixin使用
这个mixin的翻版,主要用来分离处理列表数据逻辑 我用了覆写模式 创建mixin ListMoreDataMixin // 由于没有超类的限制这里要判断下 function ____checkGet ...
- 【OI向】快速傅里叶变换(Fast Fourier Transform)
[OI向]快速傅里叶变换(Fast Fourier Transform) FFT的作用 在学习一项算法之前,我们总该关心这个算法究竟是为了干什么. (以下应用只针对OI) 一句话:求多项式 ...
- python进阶(3)序列化与反序列化
序列化与反序列化 按照某种规则,把内存中的数据保存到文件中,文件是一个字节序列,所以必须要把内存数据转换成为字节序列,输出到文件,这就是序列化:反之,从文件的字节恢复到内存,就是反序列化: pytho ...
- Content type 'application/json;charset=UTF-8' not supported异常的解决过程
首先说一下当时的场景,其实就是一个很简单的添加操作,后台传递的值是json格式的,如下图 ,后台对应的实体类, @Data @EqualsAndHashCode(callSuper = false) ...
- 正月十五吃汤圆CountDownLatch
CountDownLatch实际应用 今天是正月十五,给大家拜个晚年啦! 元宵节是中国传统节日,吃汤圆不能少啊,今天我们统计下"叫练"吃汤圆时间,并用代码模拟下叫练吃汤圆!其中用到 ...