https://www.cnblogs.com/JzedyBlogs/p/10208295.html

写得非常好:

这个是Java1.8

--------------------------------

 public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
} static final int hash(Object key) {//hash函数,用于索引定位
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
} final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;//存储数据Node没有初始化,此时初始化
if ((p = tab[i = (n - 1) & hash]) == null)//(n-1)&hash用于定位,若为null,表明Node数组该位置没有Node对象,即没有碰撞
tab[i] = newNode(hash, key, value, null);//对应位置添加Node对象
else {//表明对应位置是有Node对象的,hash碰撞了
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))//碰撞了,且桶中第一个节点就匹配
e = p;//记录第一个节点
else if (p instanceof TreeNode)//碰撞了,第一个节点没有匹配上,且桶为红黑树结构,调用红黑树结构方法添加映射
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {//碰撞了 不为红黑树结构,那么是链表结构
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {//如果到了链表尾端
p.next = newNode(hash, key, value, null);//链尾添加映射
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st//链表长度大于TREEIFY_THRESHOLD值,转换为红黑树结构
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))//如果找到重复的key,判断该节点和要插入的元素key是否相等,如果相等,出循环
break;
p = e;//为了遍历,和e = p.next结合来遍历
}
}
if (e != null) { // existing mapping for key//key映射的节点不为空
V oldValue = e.value;//取出节点值记录为老的节点值
if (!onlyIfAbsent || oldValue == null)//如果onlyIfAbsent为false,或者老的节点值为null,赋予新的值
e.value = value;
afterNodeAccess(e);//访问后回调
return oldValue;
}
}
++modCount;//结构性修改记录
if (++size > threshold)//判断是否需要扩容
resize();
afterNodeInsertion(evict);//插入后回调
return null;
}

put流程

1.通过hash函数计算key的hash值,调用putVal方法

2.如果hash表为空,调用resize()方法创建一个hash表

3.根据hash值索引hash表对应桶位置,判断该位置是否有hash碰撞

  3.1 没有碰撞,直接插入映射入hash表

  3.2 有碰撞,遍历桶中节点

    3.2.1 第一个节点匹配,记录该节点

    3.2.2 第一个节点没有匹配,桶中结构为红黑树结构,按照红黑树结构添加数据,记录返回值

    3.2.3 第一个节点没有匹配,桶中结构是链表结构。遍历链表,找到key映射节点,记录,退出循环。
      没有则在链表尾部添加节点。插入后判断链表长度是否大于转换为红黑树要求,符合则转为红黑树结构

    3.2.4 用于记录的值判断是否为null,不为则是需要插入的映射key在hash表中原来有,替换值,返回旧值putValue方法结束

public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}

static final int hash(Object key) {//hash函数,用于索引定位int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;//存储数据Node没有初始化,此时初始化if ((p = tab[i = (n - 1) & hash]) == null)//(n-1)&hash用于定位,若为null,表明Node数组该位置没有Node对象,即没有碰撞
tab[i] = newNode(hash, key, value, null);//对应位置添加Node对象else {//表明对应位置是有Node对象的,hash碰撞了
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))//碰撞了,且桶中第一个节点就匹配
e = p;//记录第一个节点elseif (p instanceof TreeNode)//碰撞了,第一个节点没有匹配上,且桶为红黑树结构,调用红黑树结构方法添加映射
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {//碰撞了 不为红黑树结构,那么是链表结构for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {//如果到了链表尾端
p.next = newNode(hash, key, value, null);//链尾添加映射if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st//链表长度大于TREEIFY_THRESHOLD值,转换为红黑树结构
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))//如果找到重复的key,判断该节点和要插入的元素key是否相等,如果相等,出循环break;
p = e;//为了遍历,和e = p.next结合来遍历
}
}
if (e != null) { // existing mapping for key//key映射的节点不为空
V oldValue = e.value;//取出节点值记录为老的节点值if (!onlyIfAbsent || oldValue == null)//如果onlyIfAbsent为false,或者老的节点值为null,赋予新的值
e.value = value;
afterNodeAccess(e);//访问后回调return oldValue;
}
}
++modCount;//结构性修改记录if (++size > threshold)//判断是否需要扩容
resize();
afterNodeInsertion(evict);//插入后回调returnnull;
}

Java HashMap的put操作(Java1.8)的更多相关文章

  1. Java HashMap的put操作(Java1.6)

    https://www.cnblogs.com/skywang12345/p/3310835.html // 存储数据的Entry数组,长度是2的幂. // HashMap是采用拉链法实现的,每一个E ...

  2. HashMap源码解析(java1.8.0)

    1.1 背景知识 1.1.1 红黑树 二叉查找树可能因为多次插入新节点导致失去平衡,使得查找效率低,查找的复杂度甚至可能会出现线性的,为了解决因为新节点的插入而导致查找树不平衡,此时就出现了红黑树. ...

  3. [翻译]Java HashMap工作原理

    大部分Java开发者都在使用Map,特别是HashMap.HashMap是一种简单但强大的方式去存储和获取数据.但有多少开发者知道HashMap内部如何工作呢?几天前,我阅读了java.util.Ha ...

  4. java对cookie的操作

    java对cookie的操作比较简单,主要介绍下建立cookie和读取cookie,以及如何设定cookie的生命周期和cookie的路径问题. 建立一个无生命周期的cookie,即随着浏览器的关闭即 ...

  5. Java学习笔记(二二)——Java HashMap

    [前面的话] 早上起来好瞌睡哈,最近要注意一样作息状态.       HashMap好好学习一下. [定义] Hashmap:是一个散列表,它存储的内容是键值对(key——value)映射.允许nul ...

  6. java集合框架之java HashMap代码解析

     java集合框架之java HashMap代码解析 文章Java集合框架综述后,具体集合类的代码,首先以既熟悉又陌生的HashMap开始. 源自http://www.codeceo.com/arti ...

  7. 【转】Java HashMap工作原理(好文章)

    大部分Java开发者都在使用Map,特别是HashMap.HashMap是一种简单但强大的方式去存储和获取数据.但有多少开发者知道HashMap内部如何工作呢?几天前,我阅读了java.util.Ha ...

  8. 【转】Java HashMap 源码解析(好文章)

    ­ .fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wra ...

  9. 转:Java HashMap实现详解

    Java HashMap实现详解 转:http://beyond99.blog.51cto.com/1469451/429789 1.    HashMap概述:    HashMap是基于哈希表的M ...

随机推荐

  1. day24 Pythonpython 面向对象设计 类

    将一些相同特征和动作的成为类,现有类才能创建对象,对象就是特征和动作的结合体 类:把一类事物的相同特征和动作整合到一起就是类.类是一个抽象概念 对象:就是基于类而创建的一个具的事物(具体存在的),也是 ...

  2. 操作循环的关键字switch,break,return的应用及区别

    break 使用break结束循环  break可以终止循环 和 switch语句的运行; break用于结束一个循环,即跳出循环体,执行循环体之后的代码: switch 使用continue提前结束 ...

  3. Linux内存管理 一个进程究竟占用多少空间?-VSS/RSS/PSS/USS

    关键词:VSS.RSS.PSS.USS._mapcount.pte_present.mem_size_stats. 在Linux里面,一个进程占用的内存有不同种说法,可以是VSS/RSS/PSS/US ...

  4. Scripts may close only the windows that were opened by it

    关闭当前窗体报以下js错误: Scripts may close only the windows that were opened by it (脚本只能关闭由它打开的窗口) 使用场景,在js中关闭 ...

  5. Maven的porfile与SpringBoot的profile结合使用详解

        使用maven的profile功能,我们可以实现多环境配置文件的动态切换,可参考我的上一篇博客.但随着SpringBoot项目越来越火,越来越多人喜欢用SpringBoot的profile功能 ...

  6. Django 的路由层 视图层 模板层

    --------------------------------------------------------------通过苦难,走向欢乐.——贝多芬 Django-2的路由层(URLconf) ...

  7. 009-定时关闭弹出广告窗口 By BoAi 20190414

    ;~ 定时关闭弹出广告窗口 By BoAi 20190414 ; ### 参数设置段 ######################################SingleInstance,forc ...

  8. c++入门之再话命名空间的意义

    c++中使用了命名空间这一概念,通过下面这个代码,我们将深刻认识到命名空间的重要作用和意义: # include"iostream" using namespace std; na ...

  9. CodeForces Round #529 Div.3

    http://codeforces.com/contest/1095 A. Repeating Cipher #include <bits/stdc++.h> using namespac ...

  10. JDK8 的FullGC 之 metaspace

    JDK8 的FullGC 之 metaspace - 简书https://www.jianshu.com/p/1a0b4bf8d498