稍稍解读下JDK8的HashMap
首先,源码中上来就有一大段注释,但最重要的就是第一句。
大意如下:
本map经常用作一个 binned (bucketed) hash table (下面有解释),但是,当bins很大的时候,它们会被转换成 bins of TreeNodes,每个bin的结构类似于TreeMap。
先解释下这里的bin,直译是容器、箱子。其实这里你可以认为它代表一个hash值对应的位置!
HashMap使用 HashMap.Node<K, V> 来存储具体的key和value,又用 Node[] 来存储Node。
transient Node<K,V>[] table;
要注意,这个Node实际上是链表结构的:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; //存储hash
final K key; //存储key
V value; //存储value
Node<K,V> next; //存储下一个Node,所以是链表结构
//其他略
}
如果不考虑hash碰撞,每当map.put(k, v)时,实际上是构建了一个Node,再将Node放到Node数组(也就是table)中。
当然位置是经过计算的:
table[(n-1) & hash] = newNode; //注意,不是数组的最后一个位置
但是,难免出现hash碰撞,也就是一个hash值有多个对象(或者多个对象的hash值一致)!这时候该怎么做?
其实在JDK7及之前,都是直接使用Node.next来安置的,也就是说,将新的对象追加到链表的尾部。
但是,JDK8有所改变,就是本文开头那句话讲述的内容。
直接看源码好了(对JDK的源码是恨得咬牙切齿,因为各种不人性化的东西):
/**
* Implements Map.put and related methods
*
* @param hash k的哈希值
* @param key k
* @param value 要存储的v
* @param onlyIfAbsent true则不修改现有的值
* @param evict false 则 table是在creation mode。
* @return 前值,或null(如果没有前值)
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K,V>[] tab; //指向 HashMap的 table。transient Node<K,V>[] table;
Node<K,V> p; //用于指向 table 中的Node对象,如果该对象还有next,则最终会指向next。--当成prev比较合适,因为要插入的对象最终会放在它后面!
int n, i; //n是table的长度;i是table中的索引。
if ((tab = table) == null || (n = tab.length) == 0) //如果table为空(续)
n = (tab = resize()).length; //则重新分配 if ((p = tab[i = (n - 1) & hash]) == null) //(n-1)&hash 用于计算新对象的索引位置,如果该位置上的对象为null(续)
tab[i] = newNode(hash, key, value, null); //直接创建Node,放到该位置上!
else { //如果位置上已有对象(续)
Node<K,V> e;
K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k)))) //如果hash和key还一样(续)
e = p;
else if (p instanceof TreeNode) //如果hash和key不一样,就判断是否是TreeNode对象(续)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); //是TreeNode对象的话,就调用putTreeVal(..)!
else { //到了这里,说明既不是同一个对象,也不是TreeNode,(续)
for (int binCount = 0; ; ++binCount) { //那就来个循环
if ((e = p.next) == null) { //如果当前Node没有next,
p.next = newNode(hash, key, value, null); //新建Node,并添加到当前Node.next
if (binCount >= TREEIFY_THRESHOLD - 1) //再判断链表的长度是否超出限制
treeifyBin(tab, hash);//超出指定长度,则将该链表转成树!
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) //这里是判断当前链表中的Node存储的对象,与要存储的对象是否同一个
break;
p = e; //沿着链表前进一个位置。等同于p=p.next
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
源码注释不太方便啊,难道要来个流程图?还是算鸟,赶紧睡觉啦(*^_^*)
稍稍解读下JDK8的HashMap的更多相关文章
- 稍稍解读下ThreadPoolExecutor
# 说说ThreadPoolExecutor ## 认识 先来看看它所在的架构体系: ```java package java.util.concurrent; public interface Ex ...
- JDK7与JDK8中HashMap的实现
JDK7中的HashMap HashMap底层维护一个数组,数组中的每一项都是一个Entry transient Entry<K,V>[] table; 我们向 HashMap 中所放置的 ...
- 【JDK8】HashMap集合 源码阅读
JDK8的HashMap数据结构上复杂了很多,因此读取效率得以大大提升,关于源码中红黑树的增删改查,博主没有细读,会在下一篇博文中使用Java实现红黑树的增删改查. 下面是类的结构图: 代码(摘抄自J ...
- 踩坑了,JDK8中HashMap依然会死循环!
是否你听说过JDK8之后HashMap已经解决的扩容死循环的问题,虽然HashMap依然说线程不安全,但是不会造成服务器load飙升的问题. 然而事实并非如此.少年可曾了解一种红黑树成环的场景,=v= ...
- Windows 7 x64环境下JDK8安装过程
Windows 7 x64环境下JDK8安装过程 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads ...
- Windows10下JDK8的下载安装与环境变量的配置
Windows10下JDK8的下载安装与环境变量的配置 下载JDK8(64位) 链接:https://pan.baidu.com/s/10ZMK7NB68kPORZsPOhivog 提取码:agsa ...
- 深入分析 JDK8 中 HashMap 的原理、实现和优化
HashMap 可以说是使用频率最高的处理键值映射的数据结构,它不保证插入顺序,允许插入 null 的键和值.本文采用 JDK8 中的源码,深入分析 HashMap 的原理.实现和优化.首发于微信公众 ...
- jdk7中hashmap实现原理和jdk8中hashmap的改进方法总结
1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端. 数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O(1 ...
- weex官方demo weex-hackernews代码解读(下)
weex 是阿里出品的一个类似RN的框架,可以使用前端技术来开发移动应用,实现一份代码支持H5,IOS和Android.而weex-hacknews则是weex官方出品的,首个使用 Weex 和 Vu ...
随机推荐
- 图解 Java 内存模型
图解 Java 内存模型 (图片来自于:http://www.cnblogs.com/zhangs1986/p/7903722.html)
- python生成指定文件夹目录树
# -*- coding: utf-8 -*- import sys from pathlib import Path class DirectionTree(object): "" ...
- PHP算式验证码和汉字验证码的实现方法
在PHP网站开发中,验证码可以有效地保护我们的表单不被恶意提交,但是如果不使用算式验证码或者汉字验证码,仅仅使用简单的字母或者数字验证码,这样的验证码方案真的安全吗? 大家知道简单数字或者字母验证码很 ...
- Saltstack 命令
命令格式 salt '<操作目标>' <方法>[参数] 查看被控主机内存使用情况 [root@node1 ~]# salt '*' cmd.run 'free -h' node ...
- cocos2d-x入门学习--准备篇
1.Cocos2D最早是一款用Python语言开发的游戏引擎.Cocos2D是一个开源框架,用于构建二维游戏,演示程序和其他图形界面交互应用等. 2.x的包含两个意思:一方面是C++的文件扩展为CXX ...
- jd-gui的使用方法
java的反编译工具,简单使用: 打开文件.单击“file”从中选择“Open File ...“选项,弹出一个文件选择框,可以选择要打开的文件,或者直接单击文件夹图标,直接弹出文件选择框:从文件选择 ...
- 话说extern和static
以前对extern.static的一些东西一直模棱两可.今天好好来梳理了一番.. static关键字 被static修饰的变量或函数称之为静态成员.函数. 存储位置:static修饰的变量存放在静态区 ...
- Unity3d之截图方法
http://blog.csdn.net/highning0007/article/details/37991787 Unity3d之截图方法 分类: Unity3D2013-11-28 17:13 ...
- Cena评测系统在win10中测评cpp程序
1.装了cena-0.8.1-20110710-setup.exe 2.打补丁:cena-0.8.2-patch.zip 3.菜单->工具->选项->编程语言 修改G++ (mi ...
- java command line error opening registry key 'Software\JavaSoft\Java Runtime Environment' java.dll
C:\Users\huxxxxchan>javaError: opening registry key 'Software\JavaSoft\Java Runtime Environment'E ...