从源码中分析HashMap的get跟put方法

话不多说直接上代码

put

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;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            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
                            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);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

#get

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;
    }

问题所在

对于2个方法的逻辑看的不是很懂虽然大概知道,先通过hash去找,如果找不到一样的hash就新建Entry,如果找到一样的判断key是否也相等,如果相等则覆盖value,如果不想等说明是key不相同但是hash相同,那么生成Entry赋值给父的next。本人的逻辑理解是这样的,但是对源码还是看不懂。
1. ,put方法中

if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            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
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }

这段逻辑不是很懂。

2.get方法中

if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);

这一段不是很理解,求解释。越详细越好。在写笔记看到这里突然卡住了,求支招。哈哈哈。

0

4个回答

  1. if ((tab = table) == null || (n = tab.length) == 0)
    1. n = (tab = resize()).length;
    2. if ((p = tab[i = (n - 1) & hash]) == null)
    3. tab[i] = newNode(hash, key, value, null);
    4. else {
    5. Node e; K k;
    6. if (p.hash == hash &&
    7. ((k = p.key) == key || (key != null && key.equals(k))))
    8. e = p;
    9. else if (p instanceof TreeNode)
    10. e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
    11. else {
    12. for (int binCount = 0; ; ++binCount) {
    13. if ((e = p.next) == null) {
    14. p.next = newNode(hash, key, value, null);
    15. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
    16. treeifyBin(tab, hash);
    17. break;
    18. }
    19. if (e.hash == hash &&
    20. ((k = e.key) == key || (key != null && key.equals(k))))
    21. break;
    22. p = e;
    23. }
    24. } 解读这一段源码:
  2. java1.8中,map的结构是由Node[] + 链表组成的,和之前的主要区别是,链表部分,当超过8个元素会转成TreeNode对象,由TreeBin封装,也就是链表转换为树形结构便于存储和查找。不是线程安全类,但是如果多线程操作会报出ConCurrentxxxException。

下面按行数来说:
1. 如果当前Node[] 为null 或者 Node数组元素个数为0 执行下面一行代码(省略大括号)
2. resize() 方法返回一个扩容后的Node[],扩容的机制为所需容量的两倍,是原有的1.5倍,这个计算是由负载因子 static final float DEFAULT_LOAD_FACTOR = 0.75f; 来决定的(具体的可以看一下resize()方法,如果有困难我们再讨论一次)。 Node数组元素个数n 赋值为扩容后的长度。
4. 「 (n - 1) & hash] 」定位数组下标位置的经典算法,你可以自己写一个main方法来做几个实验。n=16的话,计算结果肯定在0~15之间。p为计算后取到的一个Node节点 判断是否为null (if else 省略大括号)
5. 如果上述判断为true,那么当前节点赋值为一个新建节点 newNode(x,x,x,x); 这个不用说吧。也是一个经典数据结构。
6. 如果4 中判断结果为false,证明当前下标为i的位置存在节点,那么进行下列操作。
7、8、9. 如果当前node节点的hash 和 节点中key属性值 双等或值等(也就是进行新旧值替换),则将当前节点赋值给e。
10、11. 如果当前节点为TreeNode节点,则将当前操作元素添加到TreeNode[]中(图中,单独的hash,key,value都是当前操作元素的值)。
12. 如果上述都不成立,直接执行下列代码(到这步的意思是,不会是覆盖操作,当前节点也不是treeNode,只能是将当前节点拼接在列表末端)。
13-23. 这是一个看似无限循环的算法,这有几个退出点,按顺序来,第一个break,代表找到了列表末端,把当前元素添加到末端即可。第二个break,是找到新旧值覆盖的元素,进行替换。

14行为常规列表遍历。
15行为如果找到最后一个元素了,直接新建元素,将最后一个元素与新元素逻辑关联。
16行 判断当前个数,是否大于或者等于7,如果是,则将列表转化为TreeBin包装类,其实列表转树的界限是8,但是数组下标是从0开始,所以是TREEIFY_THRESHOLD - 1。
17行就是转化方法了。

用有道笔记纯手打!

0
qq_27156775
私奔Sven 到了第8个,不是让链表过长,这部分是jdk8做的优化,增快查询效率等。其他理解没问题。记住一点,他初始化的size不是16,而是0,也就是{},当有写操作的时候才会赋值16(也就是1<<4),这个你可以看源码来理解。
一年多之前 回复
StackTc
StackTc 回复私奔Sven: 看了下,我说下我的理解,1:如果数组为空直接赋值 2:如果hash跟key都一样覆盖 3:如果hash值一样key不一样,创建链表。到了第8个的时候由于不让链表过长。就以树的形势存储数据,
一年多之前 回复
qq_27156775
私奔Sven 这些都是经典,以jdk8为主,7也要看,因为你要知道它什么这么优化和怎么优化的。
一年多之前 回复
StackTc
StackTc 回复qq_27156775: 晚上我回去看一下1.8源码,再回复你,还有问下,看源码建议看1.7还是1.8?
一年多之前 回复
qq_27156775
私奔Sven 首先你要知道,jdk8之后,hashmap中,数据结构是Node[]+(链表|树),Node[]肯定是不会改变的了,那什么时候用链表,什么时候是树呢?他们有个临界点,8,这个数字我在回答里也说过,你在源码里也能找到。知道这个8了,我们看下怎么转化的,首先,数组中的一个元素,他是从链表就够开始构造的,也就是说,你连续put相同的(lenght-1)&hash的值,当个数小于8的时候,用的都是链表,也是就是node.next=new Node()形式,当个数大于等8的时候,将链表转化为TreeNode形式,p instenceof NodeTree 就是用来判断,让数组元素,是链表还是树,如果返回时true,那么,当前元素下的结构已经是树,就用树的方式将当前操作的对象加入到树中,反之还是链表,将当前操作的对象,加入到链表末尾,加入完成之后,判断是否需要转化为树结构,判断条件是什么呢,就是那个8.
一年多之前 回复
StackTc
StackTc 还是不是很理解TreeNode 这玩意,我目前的理解是newNode为增加一个链表数据或者节点,10.11.12行代码 还不是很理解
一年多之前 回复
qq_27156775
私奔Sven 对的,jdk8还优化了扩容相关的。
一年多之前 回复
StackTc
StackTc 回复qq_27156775: 这是jdk1.8新加的?我看1.7都是Entry链表形式的
一年多之前 回复
qq_27156775
私奔Sven 回复StackTc: 我提到过,当链表的size大于8.下标大于等于7的时候就要转换为TreeNode对象进行存储,在map中,它托管给了TreeBin,所以‘p instanceof TreeNode’ 这个就是判断,数组中的这个元素,是链表还是TreeNode存储,或者说结构。如果是TreeNode,直接采用putTreeVal()方法将当前操作的对象插入。有不懂的可以继续问,有错误,欢迎提出
一年多之前 回复
StackTc
StackTc else if (p instanceof TreeNode) e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
一年多之前 回复
StackTc
StackTc else if (p instanceof TreeNode) 这句话可以分析一波吗
一年多之前 回复
qq_27156775
私奔Sven 如有不对,欢迎指出~
一年多之前 回复

首先你要知道HashMap最基本的原理,就是把要存储的所有键做Hash,映射到一个数组中去。

但是有可能两个甚至更多的键映射到了同一个位置,因此HashMap需要解决的最核心的问题就来了,就是“哈希冲突”。

jdk7及以前的做法比较简单,如果出现哈希冲突,就在出现冲突的数组位置上创建一个链表,存储所有冲突的对象。这样的问题是,如果冲突的对象很多,那么这个链表会很长,会导致HashMap的性能大大下降。

因此在jdk8发布的时候做了一个优化,在冲突较少的时候依然使用链表,冲突较多的时候,就把链表转换成一棵红黑树。

这也就是你在看代码的时候看到的Node(链表节点)和TreeNode(红黑树节点)。

1
yizishou
yizishou 回复StackTc: 恩,他说的更具体
一年多之前 回复
StackTc
StackTc 感谢,你说的很正确,但是我还是打算把分给楼上。
一年多之前 回复

还有哈希表 为什么比数据查找速度快

0
StackTc
StackTc 回复whb3299065: 谢谢
一年多之前 回复
whb3299065
whb3299065 if ((p = tab[i = (n - 1) & hash]) == null)看这句其中i = (n - 1) & hash ,就是计算到哈希值对应的位置,可以看到,它并没有对表结构进行遍历,而是通过计算可以直接得到位置在,只有在该位置被其他元素占用后才会遍历查找,效率是O(1)-O(N)之间,大部分都可以在O(1)的复杂度内找到
一年多之前 回复

还有问下,为什么别人的博客里面写的源码,跟我本地看的不一样,它的简单,我的好复杂。

-1
StackTc
StackTc 回复whb3299065: 好像是的 我mac装的1.8,但是 还是需要去理解。不能说难,就退
一年多之前 回复
whb3299065
whb3299065 可能是版本问题吧,我记得1.8的Map变化挺大
一年多之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
其他相关推荐
HashMap中的数据结构与get,put源码解析
HashMap 执行流程:   首先构造方法: public HashMap() {         this.loadFactor = DEFAULT_LOAD_FACTOR; // all otherfields defaulted     } public HashMap(int initialCapacity) {         this(initialCapacit
基于源码(jdk1.7)对HashMap的get()和put()的小结
HashMap的概述 基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代 collecti
HashMap的put方法源码解析_JDK1.7
建议大家把代码复制到开发工具中,我使用的是IntelliJ_IDEA,很好用、智能。 之后跟着put的主体方法,梳理各个小的方法,遇到加粗标注,便可转移方法。 /** * @Date:Create in 14:16 2018/2/5 * @Description:详解JDK1.7的HashMap.put方法 */ public class HashMap_put { //全局数...
JDK8:HashMap源码解析:put方法
一、概述 Map的put方法接受两个参数,key和value,该方法用于存储键值对。 HashMap的put方法只有一行代码:   return putVal(hash(key), key, value, false, true); //参见:hash方法解析hash方法解析   可知put方法是一个方便用户使用的快捷方式,具体逻辑都是在putVal方法中实现的,我们就针对putVa...
HashMap中put与get的工作原理
一、Put : 让我们看下put方法的源码实现: /** * Associates the specified value with the specified key in this map. If the * map previously contained a mapping for the key, the old value is * replaced. *
HashMap的put和get方法实现原理
1、HashMap的底层源码实现put方法: 现根据key的hashCode(计算hash值的方法:int hash = hash(key.hashCode()),此方法加了高位运算,以防止hash冲突)重新计算hash值,然后再根据该hash值得到这个元素在数组中的位置(得到该hash值所对应table中索引的方法:int i = indexFor(hash,table.length))(
HashMap中put()方法实现原理
突然想解剖HashMap实现原理,Map链表的作者源码如何实现?也可以丰富一下自己的编程思想,也想让读者看见如何观看别人源码的思路和方法。所以心血来潮的我,就来解析HashMap底层原理! 送给读者的话:一个合格的程序员一定要学会观看别人代码。这样子自己的开发也会很多思路和方法。希望国内软件开发越来越好。 首先看类 public class HashMap&amp;lt;K,V&amp;gt; ...
HashMap 刚学完1.7 1.8就来了 分享一个put方法
put方法HashMap首先呢它是一个集合类,非线程安全,key、value(键值)对存储格式。常见的api有put,get,size,remove等方法在此呢介绍一下jdk1.8中的hashmap.put方法,我们在使用put方法的时候会传进key和value参数在我们将这两个参数传入后,第一步,我们的put方法会去判断这个hashmap是否为null 或者长度是否为0,若为null或者长度为0...
HashMap源码注解 之 put()方法(六)
注意 , 本文基于JDK 1.8 HashMap#put() /** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for the key, the old * value is r
HashMap的put和get方法原理
概述JAVA中的数组,在添加或者删除元素的时候,都会复制一个新数组,比较耗内存。但是数组的遍历则是非常高效的。链表则是相反, 遍历慢(需要遍历数组,一直找到值相等的元素才算找到),而添加和删除元素代价低。有没有办法结合两者的特点,做到寻找元素快,插入元素或者删除元素代价低呢?答案是利用哈利表。HashMap put操作当使用HashMap的put方法的时候,有两个问题要解决:1、长度为16的数组中
HashMap原理及put与get方法调用过程
HashMap的原理 HashMap的数据结构为数组+链表,以key,value的形式存值,通过调用put与get方法来存值与取值。 它内部维护了一个Entry数组,得到key的hashCode值将其移位按位与运算,然后再通过跟数组的长度-1作逻辑与运算得到一个index值来确定数据存储在Entry数组当中的位置,通过链表来解决hash冲突问题。当发生碰撞了,对象将会储存在链表的下一个节点中。
HashMap的put、get方法分析与Hash冲突的分析、解决
HashMap的put、get方法分析与Hash冲突的分析、解决
Java HashMap的数据结构以及put和get方法
1 HashMap的数据结构 HashMap实际上是一个链表数组,也就是最外层是数组,数组的元素是链表。    2 HashMap的put方法: 源代码如下:   public V put(K key, V value) { //1 如果Key为Null 则put到Key为null的位置 if (key == null) return p
HashMap的put和get方法介绍
相关集合介绍见:https://blog.csdn.net/fz13768884254/article/details/82905249 JAVA中的数组,在添加或者删除元素的时候,都会复制一个新数组,比较耗内存。但是数组的遍历则是非常高效的。链表则是相反,遍历慢(需要遍历数组,一直找到值相等的元素才算找到),而添加和删除元素代价低。 有没有办法结合两者的特点,做到寻找元素快,插入元素或者删除...
HashMap是如何实现put与get方法的以及到底什么时候添加数据才扩容
HashMap就是数组加链表,不同的hash放到不同的索引上,相同的hash数据放到同一个索引处,并将原索引处的数据放到新加入数据的成员变量entry上。 HashMap这个类,有个内部类叫做Entry&amp;amp;amp;amp;lt;K,V&amp;amp;amp;amp;gt;,存储了 key,value,hash,Entry&amp;amp;amp;amp;lt;K,V&amp;amp;amp;amp;gt; entry,就是key值,value值,hash值,跟下一个entry对象。 然后
手写HashMap,实现put,get以及扩容
很晚了不多说了,直接贴代码,看完就会了解HashMap是如何实现数组+链表存储的,希望能给大家带来帮助,如果有疑问和纠正,请留言,第一时间回复 package coom.michelle.dream.myhashmap; import java.util.Objects; public class MyHashMap { public Node[
JDK8:HashMap源码解析:get方法、containsKey方法、getNode方法
一、概述 HashMap存储的键值对,用put(K,V)方法来存储,用get(K)方法来获取V,用containsKey(K)方法来检查K是否存在。可先参见:put方法解析 来了解键值对的存储原理,再来看get方法就很容易了。 二、方法解析 /** * 获取key对应的值,如果找不到则返回null * 但是如果返回null并不意味着就没有找到,也可能key对应的值就是null,因为Hash...
java中HashMap详解(从源码角度看内部实现)
HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实现类。虽然 HashMap 和 HashSet 实现的接口规范不同,但它们底层的 Hash 存储机制完全一样,甚至 HashSet 本身就采用 HashMap 来实现的。  通过 HashMap
自定义实现HashMap的put、get方法
 public class HashMap{ public static void main(String[] args){ put(&quot;aa&quot;, &quot;wo ai ni&quot;); System.out.println(get(&quot;aa&quot;)); } //首先定义一个Object类型...
JDK1.7源码分析【集合】HashMap的死循环
前言 在JDK1.7&amp;amp;1.8源码对比分析【集合】HashMap中我们遗留了一个问题:为什么HashMap在调用resize() 方法时会出现死循环?这篇文章就通过JDK1.7的源码来分析并解释这个问题。 如下,并发场景下使用HashMap造成Race Condition,从而导致死循环,现象是CPU 100%被占用。 final HashMap&amp;lt;String, Strin...
7.多线程下put和get操作导致的HashMap线程不安全问题
谈谈HashMap线程不安全的体现HashMap的原理以及如何实现,之前在JDK7与JDK8中HashMap的实现中已经说明了。那么,为什么说HashMap是线程不安全的呢?它在多线程环境下,会发生什么情况呢?3个情况,1个put会同时扩容早造成死循环,2.2个put引发扩容,另外的线程有可能get不到。3.有可能2个同时put,导致1个丢失,被后1个put给覆盖掉了。         一种情况是...
Java中HashMap里的put方法源代码解析
Java中HashMap里的put方法源代码解析HashMap 源码中 put()方法详解拿到了hash值后,调用 putVal(),做了如下操作 HashMap 源码中 put()方法详解 hashmap底层结构就是数组+链表的结构,如果发生冲突,即hashcode相同key也相同,但是value不同的话,那么就会放在底层数组的同一个下标处,官方话叫同一个桶内,以链表的形式保存。 但是在jdk1...
hashmap的数据结构以及put和get
一,hashmap数据结构。数据结构中有数组和链表来实现对数据的存储,但是这两种方式的优点和缺点都很明显: 1,数组存储,它的存储区间是连续的,比较占内存,故空间复杂度高。但是利用二分法进行查找的话,效率高,时间复杂度为O(1)。其特点就是:存储区间连续,查找速度快,但是占内存严重,插入和删除就慢。 2,链表查询,它的存储区间离散,占内存比较宽松,故空间复杂度低,但时间复杂度高,为O(n)。其特
【面试库】--HashMap多线程put后get null ,get 死循环,get数据丢失(167)
0 公共put源码public V put(K key, V value) { ...... //算Hash值 int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); //如果该key已被插入,则替换掉旧的value (链接操作) for (Ent
java HashMap在不发生冲突的情况下get(key)时间复杂度是o(1)
HashMap在不发生冲突的情况下时间复杂度是o(1) 今天上课,老师布置了一道java题
HashMap源码解析(初始化及put方法)
Map初始化及put过程:首先通过默认的构造方法在堆内存中开辟一块地址。并指定默认负载因子。 HashMap底层是一个数组+链表的结构。即一个线性数组结构,Map中有一个内部Entry接口,HashMap在自己的静态内部类Node中实现了它。有三个属性key/value/next。即键值和下一指向。 当调用map的put方法时,调用hashmap的getNode方法,它返回一个Node节点。pu
[HashMap源码学习之路]---put方法中的hash方法介绍
HashMap中的put方法中的hash方法   以下是put方法的代码: public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }   当我第一次看到这个地方的时候,首先好奇的是这个hash 方法到底干了什么,于是我点了进去,看到下边这样的东西,顿时懵了: stat...
深入理解 HashMap put 方法(JDK 8逐行剖析)
前言注意:我们今天所有的一切都是基于 JDK 8,JDK 8 的实现和 JDK 7 有重大区别。前面我们分析了 hashCode 和 hash 算法的原理,其实都是为我们解析 HashMap 做铺垫,因为 HashMap 确实比较复杂(如果你每一行代码都看的话,每个位移都纠结的话),虽然总的来说,HashMap 不过是 Node 数组加 链表和红黑树。但是里面的细节确是无比的优雅和有趣。楼主为什么选
hashmap 中put实现的源代码
HashMap的数据结构: 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端。       数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难; 链表 链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。
HashMap进行put操作时遇到的并发问题
今天遇到了个问题,在全局服务器重启的时候,原有的4游戏逻辑服务器会马上再注册过来,通过多线程对HashMap进行操作,结果从后续的日志发现,id=1的数据丢失了。查了一下原因,多线程环境下put还是可能会有问题 .
HashMap的put流程(JDK8)
1、hash(key),取key的hashcode进行高位运算,返回hash值 2、如果hash数组为空,直接resize(),进行取模运算将key-value插入到数组的指定位置 3、如果数组不为空,对hash进行取模运算计算,得到key-value在数组中的存储位置i (1)如果table[i] == null,直接插入Node&amp;amp;amp;lt;key,value&amp;amp;amp;gt; (2)如果tabl...
HashMap部分源码分析
HashMap数据结构,HashMap的构造方法,HashMap的put,HashMap的get
面试题系列(一):hashmap的底层实现put操作,扩容机制
1.Hashmap原理及内部数据结构:     底部采用hash表数据加链表的形式,1.8以后引入了红黑树的数据结构。时间复杂度由O(n)变为O(logn)    节点结构: 重写了equals方法:           1.1扩容规则是什么?      初始化数组长度为16.      最大长度限制     获取在数组中的位置: 当数组长度到0.75时扩容: ...
HashMap的原理分析(2)get和remove方法的实现
public V get(Object key) { // 如果key为空,用get空key方法处理 if (key == null) return getForNullKey(); // 计算key的hash值 int hash = hash(key.hashCode()); // in
HashMap源码之get与put方法
HashMap是基于数组和链表来存储键值对对象的,我们简单看下get和put方法的源码。 1、我们调用put方法来存储键值对时,它先调用hash方法来计算hashcode,然后用hashcode和Entry数组的大小来做按位与操作,求出所在的Entry数组的下标位置。通过key与下标所在的Entry链表进行判断是否已经存在此Key,如果存在就重新设置value为新的值,不存在则加入到Entry链...
HashMap源码分析(四)put-jdk8-红黑树的引入
HashMap jdk8以后他的逻辑结构发生了一点变化: 大概就是这个意思: 当某一个点上的元素数量打到一定的阈值的时候,链表会变成一颗树,这样在极端情况下(所有的元素都在一个点上,整个就以链表),一些操作的时间复杂度有O(n)变成了O(logn)。 分析源代码; 一.还是先看下put方法,证明一下上面的图基本是对的: public V put(K key
HashMap中put方法底层源码理解
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {         Node&amp;lt;K, V&amp;gt;[] tab;         Node&amp;lt;K, V&amp;gt; p;         int n, i;         //判断tab是否已经创建,如果没有就初始化一个数组    ...
用例子解释关于HashMap的几个使用方法put,replace,merge等
HashMap做为一种好的算法工具, 由Key和Value结队组成,一个Key对应一个Value,可以成对的加入到HashMap中,只要用map.get(key)就可以得到对应的Value值。 非常适合通过学号查找成绩,通过编号查找书籍等等,所以其中Key是不能重复的,否则后面的会覆盖前面的,这里我们通过一个例子来介绍几个有效的使用方法 import java.util.HashMap; impo...
JDK1.7之 HashMap 源码分析
JDK1.7 及之前的版本中,HashMap中通过**散列链表**的形式来存储数据,基于一个数组及多个链表的方式,当hash值冲突的时候,就会在对应的节点以链表的形式存储这些hash值冲突的数据! 从上面的分析可以得到以下结论: - HashMap的value可以为null - HashMap是非线程安全的 - 初始容量和加载因子会影响HashMap的性能
ConcurrentHashMap内部结构和put remove方法分析
一下文章转自: http://www.cnblogs.com/dolphin0520/p/3932905.html ConcurrentHashMap可以做到读取数据不加锁,并且其内部的结构可以让其在进行写操作的时候能够将锁的粒度保持地尽量地小,不用对整个ConcurrentHashMap加锁。 ConcurrentHashMap的内部结构   ConcurrentHashMap
文章热词 机器学习教程 Objective-C培训 交互设计视频教程 颜色模型 设计制作学习
相关热词 mysql关联查询两次本表 native底部 react extjs glyph 图标 java 学习源码的方法 从ng的机器学习视频中