java 8 Hashmap深入解析 —— put get 方法源码(转载)

java http 发送 put delete post get请求 是一个Java 发送http put、delete、 post、 get 请求的工具类。可用在与restful service 进行通讯的代码中。 立即下载

https://www.cnblogs.com/jzb-blog/p/6637823.html

      每个java程序员都知道,HashMap是java中最重要的集合类之一,也是找工作面试中非常常见的考点,因为HashMap的实现本身确实蕴含了很多精妙的代码设计。

  对于普通的程序员,可能仅仅能说出HashMap线程不安全,允许key、value为null,以及不要求线程安全时,效率上比HashTable要快一些。稍微好一些的,会对具体实现有过大概了解,能说出HashMap由数组+链表+RBT实现,并了解HashMap的扩容机制。但如果你真的有一个刨根问题的热情,那么你肯定会想知道具体是如何一步步实现的。HashMap的源码一共2000多行,很难在这里每一句都说明,但这篇文章会让你透彻的理解到我们平时常用的几个操作下,HashMap是如何工作的。

   要先提一下的是,我看过很多讲解HashMap原理的文章,有一些讲的非常好,但这些文章习惯于把源代码和逻辑分析分开,导致出现了大段的文字讲解代码,阅读起来有些吃力和枯燥。所以我想尝试另一种风格,将更多的内容写进注释里,可能看起来有些啰嗦,但对于一些新手的理解,应该会有好的效果。


 

  • HashMap结构

   首先是了解HashMap的几个核心成员变量(以下均为jdk源码):

复制代码
 1   transient Node<K,V>[] table;        //HashMap的哈希桶数组,非常重要的存储结构,用于存放表示键值对数据的Node元素。
 2 
 3   transient Set<Map.Entry<K,V>> entrySet;  //HashMap将数据转换成set的另一种存储形式,这个变量主要用于迭代功能。
 4 
 5   transient int size;             //HashMap中实际存在的Node数量,注意这个数量不等于table的长度,甚至可能大于它,因为在table的每个节点上是一个链表(或RBT)结构,可能不止有一个Node元素存在。
 6 
 7   transient int modCount;           //HashMap的数据被修改的次数,这个变量用于迭代过程中的Fail-Fast机制,其存在的意义在于保证发生了线程安全问题时,能及时的发现(操作前备份的count和当前modCount不相等)并抛出异常终止操作。
 8 
 9   int threshold;                //HashMap的扩容阈值,在HashMap中存储的Node键值对超过这个数量时,自动扩容容量为原来的二倍。
10 
11   final float loadFactor;           //HashMap的负载因子,可计算出当前table长度下的扩容阈值:threshold = loadFactor * table.length。 
复制代码

 

  显然,HashMap的底层实现是基于一个Node的数组,那么Node是什么呢?在HashMap的内部可以看见定义了这样一个内部类:

复制代码
 1 static class Node<K,V> implements Map.Entry<K,V> {
 2   final int hash;
 3   final K key;
 4     V value;
 5     Node<K,V> next;
 6 
 7     Node(int hash, K key, V value, Node<K,V> next) {
 8         this.hash = hash;
 9         this.key = key;
10         this.value = value;
11         this.next = next;
12     }
13 
14     public final K getKey()        { return key; }
15     public final V getValue()      { return value; }
16     public final String toString() { return key + "=" + value; }
17 
18     public final int hashCode() {
19         return Objects.hashCode(key) ^ Objects.hashCode(value);
20     }
21 
22     public final V setValue(V newValue) {
23         V oldValue = value;
24         value = newValue;
25         return oldValue;
26     }
27 
28     public final boolean equals(Object o) {
29         if (o == this)
30             return true;
31         if (o instanceof Map.Entry) {
32             Map.Entry<?,?> e = (Map.Entry<?,?>)o;
33             if (Objects.equals(key, e.getKey()) &&
34                 Objects.equals(value, e.getValue()))
35                 return true;
36         }
37         return false;
38     }
39 }
复制代码

 

  我们大体看一下这个内部类就可以知道,它实现了Map.Entry接口。其内部的变量含义也很明确,hash值、key\value对和实现链表和红黑树所需要的指针索引。

  既然知道了HashMap的基本结构,那么这些变量的默认值都是多少呢?我们再看一下HashMap定义的一些常量:

复制代码
 1     //默认的初始容量为16,必须是2的幂次
 2        static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 
3 4 //最大容量即2的30次方 5 static final int MAXIMUM_CAPACITY = 1 << 30; 6 7 //默认加载因子 8 static final float DEFAULT_LOAD_FACTOR = 0.75f; 9 10 //当put一个元素时,其链表长度达到8时将链表转换为红黑树 11 static final int TREEIFY_THRESHOLD = 8; 12 13 //链表长度小于6时,解散红黑树 14 static final int UNTREEIFY_THRESHOLD = 6; 15 16 //默认的最小的扩容量64,为避免重新扩容冲突,至少为4 * TREEIFY_THRESHOLD=32,即默认初始容量的2倍 17 static final int MIN_TREEIFY_CAPACITY = 64;
复制代码

 

  • TIP : 在HashMap内部定义的几个变量,包括桶数组本身都是transient修饰的,这代表了他们无法被序列化,而HashMap本身是实现了Serializable接口的。这很容易产生疑惑:HashMap是如何序列化的呢?查了一下源码发现,HashMap内有两个用于序列化的函数 readObject(ObjectInputStream s) 和 writeObject(ObjectOutputStreams),通过这个函数将table序列化。

  •  HashMap 的 put 方法解析

  以上就是我们对HashMap的初步认识,下面进入正题,看看HashMap是如何添加、查找与删除数据的。

  首先来看put方法,我尽量在每行都加注释阐明这一行的含义,让阅读起来更容易理解。

复制代码
 1     public V put(K key, V value) {
 2         return putVal(hash(key), key, value, false, true); 
 3     }
 4 
 5   final V putVal(int hash, K key, V value, boolean onlyIfAbsent,          //这里onlyIfAbsent表示只有在该key对应原来的value为null的时候才插入,也就是说如果value之前存在了,就不会被新put的元素覆盖。
 6                    boolean evict) {                                              //evict参数用于LinkedHashMap中的尾部操作,这里没有实际意义。
 7         Node<K,V>[] tab; Node<K,V> p; int n, i;                    //定义变量tab是将要操作的Node数组引用,p表示tab上的某Node节点,n为tab的长度,i为tab的下标。
 8         if ((tab = table) == null || (n = tab.length) == 0)                    //判断当table为null或者tab的长度为0时,即table尚未初始化,此时通过resize()方法得到初始化的table。                        
 9             n = (tab = resize()).length;                        //这种情况是可能发生的,HashMap的注释中提到:The table, initialized on first use, and resized as necessary。
10         if ((p = tab[i = (n - 1) & hash]) == null)                               //此处通过(n - 1) & hash 计算出的值作为tab的下标i,并另p表示tab[i],也就是该链表第一个节点的位置。并判断p是否为null。
11             tab[i] = newNode(hash, key, value, null);                 //当p为null时,表明tab[i]上没有任何元素,那么接下来就new第一个Node节点,调用newNode方法返回新节点赋值给tab[i]。
12         else {                                              //下面进入p不为null的情况,有三种情况:p为链表节点;p为红黑树节点;p是链表节点但长度为临界长度TREEIFY_THRESHOLD,再插入任何元素就要变成红黑树了。
13             Node<K,V> e; K k;                               //定义e引用即将插入的Node节点,并且下文可以看出 k = p.key。
14             if (p.hash == hash &&                             //HashMap中判断key相同的条件是key的hash相同,并且符合equals方法。这里判断了p.key是否和插入的key相等,如果相等,则将p的引用赋给e。
15                 ((k = p.key) == key || (key != null && key.equals(k))))           //这一步的判断其实是属于一种特殊情况,即HashMap中已经存在了key,于是插入操作就不需要了,只要把原来的value覆盖就可以了。
16                 e = p;                                    //这里为什么要把p赋值给e,而不是直接覆盖原值呢?答案很简单,现在我们只判断了第一个节点,后面还可能出现key相同,所以需要在最后一并处理。
17             else if (p instanceof TreeNode)                                       //现在开始了第一种情况,p是红黑树节点,那么肯定插入后仍然是红黑树节点,所以我们直接强制转型p后调用TreeNode.putTreeVal方法,返回的引用赋给e。
18                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);   //你可能好奇,这里怎么不遍历tree看看有没有key相同的节点呢?其实,putTreeVal内部进行了遍历,存在相同hash时返回被覆盖的TreeNode,否则返回null。
19             else {                                                  //接下里就是p为链表节点的情形,也就是上述说的另外两类情况:插入后还是链表/插入后转红黑树。另外,上行转型代码也说明了TreeNode是Node的一个子类。
20                 for (int binCount = 0; ; ++binCount) {                 //我们需要一个计数器来计算当前链表的元素个数,并遍历链表,binCount就是这个计数器。
21                     if ((e = p.next) == null) {                     //遍历过程中当发现p.next为null时,说明链表到头了,直接在p的后面插入新的链表节点,即把新节点的引用赋给p.next,插入操作就完成了。注意此时e赋给p。
22                         p.next = newNode(hash, key, value, null);          //最后一个参数为新节点的next,这里传入null,保证了新节点继续为该链表的末端。
23                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st     //插入成功后,要判断是否需要转换为红黑树,因为插入后链表长度加1,而binCount并不包含新节点,所以判断时要将临界阈值减1。
24                             treeifyBin(tab, hash);                     //当新长度满足转换条件时,调用treeifyBin方法,将该链表转换为红黑树。
25                         break;                                //当然如果不满足转换条件,那么插入数据后结构也无需变动,所有插入操作也到此结束了,break退出即可。
26                     }
27                     if (e.hash == hash &&                         //在遍历链表的过程中,我之前提到了,有可能遍历到与插入的key相同的节点,此时只要将这个节点引用赋值给e,最后通过e去把新的value覆盖掉就可以了。
28                         ((k = e.key) == key || (key != null && key.equals(k))))   //老样子判断当前遍历的节点的key是否相同。
29                         break;                                //找到了相同key的节点,那么插入操作也不需要了,直接break退出循环进行最后的value覆盖操作。
30                     p = e;                                  //在第21行我提到过,e是当前遍历的节点p的下一个节点,p = e 就是依次遍历链表的核心语句。每次循环时p都是下一个node节点。
31                 }
32             }
33             if (e != null) { // existing mapping for key                //左边注释为jdk自带注释,说的很明白了,针对已经存在key的情况做处理。
34                 V oldValue = e.value;                           //定义oldValue,即原存在的节点e的value值。
35                 if (!onlyIfAbsent || oldValue == null)                 //前面提到,onlyIfAbsent表示存在key相同时不做覆盖处理,这里作为判断条件,可以看出当onlyIfAbsent为false或者oldValue为null时,进行覆盖操作。
36                     e.value = value;                              //覆盖操作,将原节点e上的value设置为插入的新value。
37                 afterNodeAccess(e);                            //这个函数在hashmap中没有任何操作,是个空函数,他存在主要是为了linkedHashMap的一些后续处理工作。
38                 return oldValue;                              //这里很有意思,他返回的是被覆盖的oldValue。我们在使用put方法时很少用他的返回值,甚至忘了它的存在,这里我们知道,他返回的是被覆盖的oldValue。
39             }
40         }                                            
41         ++modCount;                                      //收尾工作,值得一提的是,对key相同而覆盖oldValue的情况,在前面已经return,不会执行这里,所以那一类情况不算数据结构变化,并不改变modCount值。
42         if (++size > threshold)                               //同理,覆盖oldValue时显然没有新元素添加,除此之外都新增了一个元素,这里++size并与threshold判断是否达到了扩容标准。
43             resize();                                     //当HashMap中存在的node节点大于threshold时,hashmap进行扩容。
44         afterNodeInsertion(evict);                             //这里与前面的afterNodeAccess同理,是用于linkedHashMap的尾部操作,HashMap中并无实际意义。1
45         return null;                                        //最终,对于真正进行插入元素的情况,put函数一律返回null。
46     }
复制代码

    在上述代码中的第十行,HashMap根据 (n - 1) & hash 求出了元素在node数组的下标。这个操作非常精妙,下面我们仔细分析一下计算下标的过程,主要分三个阶段:计算hashcode、高位运算和取模运算。

  首先,传进来的hash值是由put方法中的hash(key)产生的(上述第2行),我们来看一下hash()方法的源码:

1     static final int hash(Object key) {
2         int h;
3         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
4     }

  这里通过key.hashCode()计算出key的哈希值,然后将哈希值h右移16位,再与原来的h做异或^运算——这一步是高位运算。设想一下,如果没有高位运算,那么hash值将是一个int型的32位数。而从2的-31次幂到2的31次幂之间,有将近几十亿的空间,如果我们的HashMap的table有这么长,内存早就爆了。所以这个散列值不能直接用来最终的取模运算,而需要先加入高位运算,将高16位和低16位的信息"融合"到一起,也称为"扰动函数"。这样才能保证hash值所有位的数值特征都保存下来而没有遗漏,从而使映射结果尽可能的松散。最后,根据 n-1 做与操作的取模运算。这里也能看出为什么HashMap要限制table的长度为2的n次幂,因为这样,n-1可以保证二进制展示形式是(以16为例)0000 0000 0000 0000 0000 0000 0000 1111。在做"与"操作时,就等同于截取hash二进制值得后四位数据作为下标。这里也可以看出"扰动函数"的重要性了,如果高位不参与运算,那么高16位的hash特征几乎永远得不到展现,发生hash碰撞的几率就会增大,从而影响性能。

  HashMap的put方法的源码实现就是这样了,整理思路非常连贯。这里面有几个函数的源码(比如resize、putTreeValue、newNode、treeifyBin)限于篇幅原因,就不贴了,后面应该还会更新在其他博客里,有兴趣的同学也可以自己挖掘一下。


  • HashMap 的 get 方法解析

   读完了put的源码,其实已经可以很清晰的理清HashMap的工作原理了。接下来再看get方法的源码,就非常的简单:

复制代码
 1     public V get(Object key) {
 2         Node<K,V> e;
 3         return (e = getNode(hash(key), key)) == null ? null : e.value;      //根据key及其hash值查询node节点,如果存在,则返回该节点的value值。
 4     }
 5 
 6     final Node<K,V> getNode(int hash, Object key) {                  //根据key搜索节点的方法。记住判断key相等的条件:hash值相同 并且 符合equals方法。
 7         Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
 8         if ((tab = table) != null && (n = tab.length) > 0 &&            //根据输入的hash值,可以直接计算出对应的下标(n - 1)& hash,缩小查询范围,如果存在结果,则必定在table的这个位置上。
 9             (first = tab[(n - 1) & hash]) != null) {
10             if (first.hash == hash && // always check first node
11                 ((k = first.key) == key || (key != null && key.equals(k))))    //判断第一个存在的节点的key是否和查询的key相等。如果相等,直接返回该节点。
12                 return first;
13             if ((e = first.next) != null) {                       //遍历该链表/红黑树直到next为null。
14                 if (first instanceof TreeNode)                       //当这个table节点上存储的是红黑树结构时,在根节点first上调用getTreeNode方法,在内部遍历红黑树节点,查看是否有匹配的TreeNode。
15                     return ((TreeNode<K,V>)first).getTreeNode(hash, key);
16                 do {
17                     if (e.hash == hash &&                        //当这个table节点上存储的是链表结构时,用跟第11行同样的方式去判断key是否相同。
18                         ((k = e.key) == key || (key != null && key.equals(k))))
19                         return e;
20                 } while ((e = e.next) != null);                      //如果key不同,一直遍历下去直到链表尽头,e.next == null。
21             }
22         }
23         return null;
24     }
复制代码

  因为查询过程不涉及到HashMap的结构变动,所以get方法的源码显得很简洁。核心逻辑就是遍历table某特定位置上的所有节点,分别与key进行比较看是否相等。


  以上便是HashMap最常用API的源码分析,除此之外,HashMap还有一些知识需要重点学习:扩容机制、并发安全问题、内部红黑树的实现。这些内容我也会在之后陆续发文分析,希望可以帮读者彻底理解HashMap的原理。

 

【零基础入门哈希签名架构,从Lamport到SPHINCS+】第二集 哈希签名树 Merkle Tree Authentication 在第一集中,我们一直忽略了一个问题。设想一下,假如有一个Hacker(黑客),在截获到Sender发送给Receiver的公钥之后,把这个公钥改成Hacker自己的公钥,然后再发给Receiver,那么Receiver就会误认为这是Sender的公钥。所以我们之前所说放方案,其安全性都需要建立在一个前提下:Receiver需要确认公钥确实是Sender发出的。 阅读详情

相关推荐

【Agora 本地录制最全总结- 基于Java SDK】

Agora 本地服务端录制

lizhengze1117的博客 1790

java 8 Hashmap深入解析 —— put get 方法源码

本文为原创博文,转载请注明出处,侵权必究! 每个java程序员都知道,HashMap是java中最重要的集合类之一,也是找工作面试中非常常见的考点,因为HashMap的实现本身确实蕴含了很多精妙的代码设计。   对于普通的程序员,可能仅仅能说出HashMap线程不安全,允许key、value为null,以及不要求线程安全时,效率上比HashTable要快一些。...

249

【开源】基于TI ADS1299与ESP32的8通道脑电采集设备YuEEG全解析

本文详细解析了基于TI ADS1299芯片与ESP32主控的开源8通道脑电采集设备YuEEG。内容涵盖硬件设计、固件驱动开发及Python上位机软件实现,提供了从电路连接、SPI通信配置到数据读取与实时波形显示的全套教程。该项目为学习脑机接口(BCI)与高精度生物电信号采集提供了完整的开源解决方案,适合创客、学生及嵌入式开发者实践。

weixin_42576410的博客 1237

Java 基础 ——HashMap构造、PUT、GET

文章目录Java 基础 —— HashMap之图解HashMap Java 基础 —— HashMap之图解HashMap HashMap的数据结构包括了初始数组、链表、红黑树; 插入数据的时候使用key%size来进行插入数据; 当两个或者两个以上的key的key相同,且key值不同的时候(即%【取余】发生冲突,就会挂在数组初始化位置的链表后) 当某个节点后出现过多的链表节点的时候,就会换成红...

Genius only means hard-working all one's life. 1187

Dify如何实现自主学习

摘要:Dify通过构建感知-决策-执行-反馈的闭环实现"自主学习",本质是打造能自动获取信息、更新知识库并调整策略的Agent工作流。核心包括:1)建立知识库作为记忆体;2)搭建感知与执行工具获取实时信息;3)通过工作流编排决策"大脑",包含LLM决策节点、行动执行节点和反馈反思节点。典型应用如根据用户反馈优化回答风格,通过存储反思日志到知识库实现持续优化。这种机制使智能体能基于新数据和经验不断自我改进。

专注于技术研发行业技术主要有AI人工智能视觉识别、物联网技术、电商平台、app原生技术、微信小程序、鸿蒙语言开发、办公企业oa、ERP等,致力于做东半球最优秀的程序员 725

Quartus II 13.0安装避坑指南:从下载到破解的完整流程(附常见错误解决方案)

本文提供Quartus II 13.0软件的详细安装与破解指南,涵盖从下载准备、路径设置、主程序安装到授权配置的全流程。重点解析了破解过程中生成和修改license.dat文件的核心步骤,并针对“无法找到有效的License文件”等常见错误提供了实用的解决方案,帮助用户快速构建稳定的FPGA开发环境。

weixin_29291863的博客 758

java restfull请求方式 (get、post、put 、delete、patch)

restfull请求方式

weixin_43652507的博客 6531

从配置字到实际运动:手把手教你用EtherCAT调试伺服电机的控制模式(以倍福TwinCAT3为例)

本文详细介绍了如何使用EtherCAT调试伺服电机的控制模式,以倍福TwinCAT3为例。从环境搭建、模式配置到状态机切换,提供了完整的操作指南和典型故障排查方法,帮助工程师快速解决‘配置字写入了但电机不转’等常见问题,提升工业自动化现场的调试效率。

weixin_42783709的博客 420

JAVA调用第三方接口的GET/POST/PUT请求方式

在工作当中调用第三方接口时面临的几种接口请求方式

qq_45726836的博客 7832

广东省低应变题库

11.按《建筑地基基础检测规范》DBJ/T15-60-2019的规定,采用低应变法对混凝土预制桩进行检测时,地基基础设计等级为甲级的桩基工程抽检桩数不应少于总桩数的30%,其余桩基工程抽检桩数不应少于总桩数的20%,且不得少于10根;16.按《建筑地基基础检测规范》DBJ/T15-60-2019的规定,当对某桩的桩身完整性判定为III类时,可认为该桩不满足验收条件,不用进一步确定该桩桩身缺陷对桩身结构承载力的影响程度。2.地基基础检测所用计量器具均应在检定(校准、验证)的有效期内,性能符合相关技术要求。

ZShy001238的博客 318

基于STM32设计的太阳能智慧路灯_444

本文设计并实现了一种基于 STM32 的太阳能智慧路灯系统。系统以 STM32F103C8T6 单片机为核心,集成温湿度检测、空气质量检测、环境光照检测、行人检测和路灯故障检测等功能,实现对路灯运行环境和工作状态的实时感知。

460

java map put get_JAVA中MAP的PUT函数和GET函数用法

没有比较完整的说明他们的用法,一般就只能看源函数,但是看起来比较的费劲。那么究竟put函数和get函数的用法是如何的呢?当然java中的Map集合是有Key和Value的。put()函数Vput(K key, V value)使用的参数:该方法有两个参数。key - 与指定值相关联的键。value - 与指定键关联的值。返回值:当存在这个key的时候,会覆盖掉原来的value并返回oldvalue...

weixin_39586235的博客 1424

vue2 - 最新详细实现高德地图绘制动态热力图详细教程,在某区域或城市地图上做“热力图层“展示人流量、旅游人口密度、热量图、全国地区地理热力图,利用颜色的深浅表示密度或强度等场景(详细示例代码教程)

vue2,nuxt2,高德地图,热力图,绘制热力图层,报错,卡顿,出不来怎么办,无法关闭,怎么解决这个问题,出错,城市名称,城市某个街道,城区,省份,市区,县城,乡镇,村,各省份的热力图,交通流量,人口流量,密度,旅游出行,人口数量,颜色介绍,深浅代表,人流量,vue2使用高德地图实现某个区域热力图,vue高德地图api加载热力图及关闭,接收热力图数据并渲染热力图,关掉热力图,Vue使用高德API制作热力图,热力图加载不出来,电子围栏围住热力图,vue2中使用高德地图渲染热力图组件,高德地图使用热力图详细教

✅ 精选推荐文章 4201

HashMap源码全解析从一道面试题说起:请一行一行代码描述下hashmap put方法

本文原创地址,我的博客:https://jsbintask.cn/2019/02/27/jdk/jdk8-hashmap-sourcecode/(食用效果最佳),转载请注明出处! 前言 前阵子(估计也快半年了吧)遇到这么一个面试题:请一行代码一行代码描述下HashMap put方法。 我:。。。 哈哈,其实也没有无语,当时知道HashMap的原理,数据结构,以及一些要注意的点,没想到面试官...

jsbintask的博客 1840

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

[源码解析]HashMap

本篇文章是我解析Java-API的笔记,更多的是从方法着手,少对类的整体情况进行说明,故并不详尽,可供大家参考。 如果文中阐述不全或不对的,多多交流。

进步*于辰的博客 1万+

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

聊聊Java源码 — HashMap(1)

起初呢,HashMap只是一个小小的数组,它拿到key之后,通过一个叫hash的算法,得到与该key对应的数组下标,并把key和value都存储到这个位置上,这就是HashMap的一个Node,计算这个下标的算法受数组长度和key值的影响,只要两者不变,位置就是固定的,所以在查询时花费的时间复杂度是O(1)。当你的key越来越多,出现两个甚至多个key被hash到同一个位置的情况,这就是hash碰撞,怎么办?

Roderick2015的博客 2064

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Java源码解析系列(四)HashMap源码解析

版权声明:本文为博主原创文章,欢迎大家转载! 但是转载请标明出处: https://blog.youkuaiyun.com/t000818/article/details/82883103 ,本文出自:【唐宏宇的博客】 备注:以下都是基于JDK8 源码分析 HashMap简介          HashMap基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 n...

一场人生,一场梦 375

HashMap源码分析_JDK1.8版本

HashMap源码分析_JDK1.8版本声明文章均为本人技术笔记,转载请注明出处 [1] https://segmentfault.com/u/yzwall [2] blog.youkuaiyun.com/j_dark/HashMap声明 public class HashMapHashMap结构图示HashMap基本数据结构 HashMap本质是一个散列表,存储元素为键值对; HashMap继承A

less is more 647

java集合框架源码解析之HashMap

摘要 HashMap简介 HashMap数据结构 HashMap源码解析 HashMap简介: HashMap是一个散列表,它的底层实现是链表数组,存储的结果为键值对,key-value形式,Key允许有一个null,value允许有多个null。 通过hash算法来快速定位该数据位于那条链表中。 HashMap继承自AbstractMap&amp;amp;amp;lt;K,V&amp;amp;amp;gt;,所以它是一个map,它实现...

qq_37520037的博客 200

HashMap put get方法

1 public V put(K key, V value) { 2 if (key == null) 3 return putForNullKey(value);//处理Null值 4 int hash = hash(key.hashCode());//获得Hashcode再处理 5 int i = index...

weixin_30247781的博客 73

[转载] JDK8 HashMap工作原理分析

【转载地址】jdk HashMap工作原理分析 Map是一个映射键和值的对象。类似于Python中的字典。 HashMap为什么会出现呢? 因为数组这种数据结构,虽然遍历简单,但是插入和删除操作复杂,需要移动数组内部的元素;链表这种数据结构,插入和删除操作简单,但是查找复杂,只能一个一个地遍历。 有没有一种新的数据结构,插入数据简单,同时查找也简单? 这个时候就出现了哈希表这种数据结构。 这是一种折中的方式,插入没链表快,查询没数组快。 wiki上就是这么定义哈希表的: 散列表(Hash tab

Young's Blog 263

底层原理Hashmap源码解析实例

Map.java 1 package com.collection; 2 3 public interface Map<K, V> { 4 public V put(K k, V v); 5 6 public V get(K k); 7 8 public int size(); 9 10 int...

dht91597的博客 357

Java集合:HashMap底层实现和原理(源码解析)【转载】

原文:https://www.cnblogs.com/java-jun-world2099/p/9258605.html Note:文章的内容基于JDK1.7进行分析。1.8做的改动文章末尾进行讲解。 一、先来熟悉一下我们常用的HashMap: 1、概述 HashMap基于Map接口实现,元素以键值对的方式存储,并且允许使用null 建和null值, 因为key不允许重复,因此只能有一个键...

lilililililydia的博客 338

java集合框架08——HashMap和源码分析

上一章总体分析了Map架构,并简单分析了一下AbstractMap源码,这一章开始我们将对Map的具体实现类进行详细的学习。本章先研究HashMap。依然遵循以下步骤:先对HashMap有个整体的认识,然后学习它的源码,深入剖析HashMap。

1万+

HashMap源码详细注释

HashMap核心源码解读 本文为CoderRuL原创,如需转载请附上本文链接! 博主主页:https://blog.youkuaiyun.com/qq_43472474 属性 public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializabl...

luoru的博客 548

Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析

文章转载于:https://javadoop.com/post/hashmap 网上关于 HashMap 和 ConcurrentHashMap 的文章确实不少,不过缺斤少两的文章比较多,所以才想自己也写一篇,把细节说清楚说透,尤其像 Java8 中的 ConcurrentHashMap,大部分文章都说不清楚。终归是希望能降低大家学习的成本,不希望大家到处找各种不是很靠谱的文章,看完一篇又一篇,...

m0_37771398的博客 232
上一篇: 《Java核心技术卷1》Chap12并发汇总
下一篇: chap8-泛型程序设计
hellohake
博客等级 码龄10年 4543粉丝 108原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值