java HashMap源码分析(JDK8)
这两天在复习JAVA的知识点,想更深层次的了解一下JAVA,所以就看了看JAVA的源码,把自己的分析写在这里,也当做是笔记吧,方便记忆。写的不对的地方也请大家多多指教。
JDK1.6中HashMap采用的是位桶+链表的方式,即我们常说的散列链表的方式,而JDK1.8中采用的是位桶+链表/红黑树的方式,也是非线程安全的。当某个位桶的链表的长度达到某个阀值的时候,这个链表就将转换成红黑树。
基本的数据结构:
- //链表节点
- static class Node<K,V> implements Map.Entry<K,V> {
- final int hash;
- final K key;
- V value;
- Node<K,V> next;
- //省略
- }
- //红黑树节点
- static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
- TreeNode<K,V> parent; // red-black tree links
- TreeNode<K,V> left;
- TreeNode<K,V> right;
- TreeNode<K,V> prev; // needed to unlink next upon deletion
- boolean red;
- TreeNode(int hash, K key, V val, Node<K,V> next) {
- super(hash, key, val, next);
- }
- //省略
- }
- // HashMap的主要属性
- public class HashMap<K,V> extends AbstractMap<K,V>
- implements Map<K,V>, Cloneable, Serializable {
- // 槽数组,Node<K,V>类型,TreeNode extends LinkedHashMap.Entry<K,V>,所以可以存放TreeNode来实现Tree bins
- transient Node<K,V>[] table;
- transient Set<Map.Entry<K,V>> entrySet;
- transient int size;
- // 去掉了volatile的修饰符
- transient int modCount;
- int threshold;
- final float loadFactor;
- ...
- }
- //计算key的hash
- static final int hash(Object key) {
- int h;
- return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
- }
get(key) 函数
- 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;
- //hash & length-1 定位数组下标
- 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) {
- /*第一个节点是TreeNode,则采用位桶+红黑树结构,
- * 调用TreeNode.getTreeNode(hash,key),
- *遍历红黑树,得到节点的value
- */
- 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;
- }
- final TreeNode<K,V> getTreeNode(int h, Object k) {
- //找到红黑树的根节点并遍历红黑树
- return ((parent != null) ? root() : this).find(h, k, null);
- }
- /*
- *通过hash值的比较,递归的去遍历红黑树,这里要提的是compareableClassFor(Class k)这个函数的作用,在某些时候
- *如果红黑树节点的元素are of the same "class C implements Comparable<C>" type
- *利用他们的compareTo()方法来比较大小,这里需要通过反射机制来check他们到底是不是属于同一个类,是不是具有可比较性.
- */
- final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
- TreeNode<K,V> p = this;
- do {
- int ph, dir; K pk;
- TreeNode<K,V> pl = p.left, pr = p.right, q;
- if ((ph = p.hash) > h)
- p = pl;
- else if (ph < h)
- p = pr;
- else if ((pk = p.key) == k || (k != null && k.equals(pk)))
- return p;
- else if (pl == null)
- p = pr;
- else if (pr == null)
- p = pl;
- else if ((kc != null ||
- (kc = comparableClassFor(k)) != null) &&
- (dir = compareComparables(kc, k, pk)) != 0)
- p = (dir < 0) ? pl : pr;
- else if ((q = pr.find(h, k, kc)) != null)
- return q;
- else
- p = pl;
- } while (p != null);
- return null;
- }
put(K key,V value)函数
- //put(K key,V value)函数
- public V put(K key, V value) {
- return putVal(hash(key), key, value, false, true);
- }
- final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
- boolean evict) {
- Node<K,V>[] tab;
- Node<K,V> p;
- int n, i;
- //如果table为空或者长度为0,则resize()
- if ((tab = table) == null || (n = tab.length) == 0)
- n = (tab = resize()).length;
- //找到key值对应的槽并且是第一个,直接加入
- if ((p = tab[i = (n - 1) & hash]) == null)
- tab[i] = newNode(hash, key, value, null);
- else {
- Node<K,V> e;
- K k;
- //第一个node的hash值即为要加入元素的hash
- if (p.hash == hash &&
- ((k = p.key) == key || (key != null && key.equals(k)))){
- e = p;
- }else if (p instanceof TreeNode)//第一个节点是TreeNode,即tree-bin
- /*Tree version of putVal.
- *final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,int h, K k, V v)
- */
- e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
- else {
- //不是TreeNode,即为链表,遍历链表
- for (int binCount = 0; ; ++binCount) {
- /*到达链表的尾端也没有找到key值相同的节点,
- *则生成一个新的Node,并且判断链表的节点个数是不是到达转换成红黑树的上界
- *达到,则转换成红黑树
- */
- if ((e = p.next) == null) {
- p.next = newNode(hash, key, value, null);
- if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
- treeifyBin(tab, hash);
- break;
- }
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k))))
- break;
- p = e;
- }
- }
- if (e != null) { // existing mapping for key
- V oldValue = e.value;
- if (!onlyIfAbsent || oldValue == null)
- e.value = value;
- afterNodeAccess(e);
- //返回旧的value值
- return oldValue;
- }
- }
- ++modCount;
- if (++size > threshold)
- resize();
- afterNodeInsertion(evict);
- return null;
- }
java HashMap源码分析(JDK8)的更多相关文章
- Java HashMap源码分析(含散列表、红黑树、扰动函数等重点问题分析)
写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...
- Java HashMap源码分析
貌似HashMap跟ConcurrentHashMap是面试经常考的东西,抽空来简单分析下它的源码 构造函数 /** * Constructs an empty <tt>HashMap&l ...
- 源码分析(一) HashMap 源码分析|JDK8
HashMap是一个普遍应用于各大JAVA平台的最最最常用的数据结构.<K,V>的存储形式使HashMap备受广大java程序员的喜欢.JDK8中HashMap发生了很大的变化,例如:之前 ...
- 【JAVA集合】HashMap源码分析(转载)
原文出处:http://www.cnblogs.com/chenpi/p/5280304.html 以下内容基于jdk1.7.0_79源码: 什么是HashMap 基于哈希表的一个Map接口实现,存储 ...
- Java源码解析——集合框架(五)——HashMap源码分析
HashMap源码分析 HashMap的底层实现是面试中问到最多的,其原理也更加复杂,涉及的知识也越多,在项目中的使用也最多.因此清晰分析出其底层源码对于深刻理解其实现有重要的意义,jdk1.8之后其 ...
- java集合源码分析(六):HashMap
概述 HashMap 是 Map 接口下一个线程不安全的,基于哈希表的实现类.由于他解决哈希冲突的方式是分离链表法,也就是拉链法,因此他的数据结构是数组+链表,在 JDK8 以后,当哈希冲突严重时,H ...
- Java集合源码分析(四)HashMap
一.HashMap简介 1.1.HashMap概述 HashMap是基于哈希表的Map接口实现的,它存储的是内容是键值对<key,value>映射.此类不保证映射的顺序,假定哈希函数将元素 ...
- 【Java】HashMap源码分析——常用方法详解
上一篇介绍了HashMap的基本概念,这一篇着重介绍HasHMap中的一些常用方法:put()get()**resize()** 首先介绍resize()这个方法,在我看来这是HashMap中一个非常 ...
- 【Java】HashMap源码分析——基本概念
在JDK1.8后,对HashMap源码进行了更改,引入了红黑树.在这之前,HashMap实际上就是就是数组+链表的结构,由于HashMap是一张哈希表,其会产生哈希冲突,为了解决哈希冲突,HashMa ...
随机推荐
- (并查集 添加关系)How Many Answers Are Wrong --Hdu --3038
链接: http://acm.hdu.edu.cn/showproblem.php?pid=3038 http://acm.hust.edu.cn/vjudge/contest/view.action ...
- Robotframework-Appium 之常用API(二)
续接上一文,更多API详细如下: 注:更多官方详情信息见 http://robotframework.org/robotframework/ 28. Name: Install App Source: ...
- 20155209 2016-2017-2 《Java程序设计》第八周学习总结
20155209 2016-2017-2 <Java程序设计>第八周学习总结 教材学习内容总结 Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从J ...
- android免root hook框架legend
一.前言 Android中hook框架已经非常多了,最优秀的当属Xposed和Substrate了,这两个框架我在之前的文章都详细介绍过了,不了解的同学,可以转战这里:http://www.wjdia ...
- Gridview的RowDataBound事件可以做很多事情
protected void gvTest_RowDataBound(object sender, GridViewRowEventArgs e) { //如果是绑 ...
- Amazon成本和产出的衡量方式
Amazon用一种T-Shirt Size 估计的方式来做项目. 产品经理会对每一条需求评估上业务影响力的尺寸,如:XXXL 影响一千万人以上或是可以占到上亿美金的市场,XXL,影响百万用户或是占了千 ...
- 查看Linux服务器被映射的公网ip
查看Linux服务器被映射的公网ip 现在云服务器非常流行,不仅企业甚至是个人都可能拥有自己的云服务器,但是目前的云服务器厂商提供的公网IP大都是映射而来,所以在Linux服务器上执行ifconf ...
- [ACM_模拟] HDU 1006 Tick and Tick [时钟间隔角度问题]
Problem Description The three hands of the clock are rotating every second and meeting each other ma ...
- LeetCode145:Binary Tree Postorder Traversal
题目: Given a binary tree, return the postorder traversal of its nodes' values. For example: Given bin ...
- javascript快速排序的思考
还记得三个月前,学习过快速排序,示例所讲的python快速排序十分易于理解,然而网上学习的c#的快速排序当时就懵逼的,现在已经全忘了,大概记得个思路 在学习完了一些高级的js方法后,今天用js模拟了p ...