zoukankan      html  css  js  c++  java
  • HashMap

    HashMap

            实现了 Map<K,V> 接口, HashTable实现了Dictionary<K,V>

            数据结构 : 数组 + 链表 + 红黑树(增加查询速度)

            基本使用方法 :

         1 . 从测试用例开始查看HashMap的源码

             当然在查看前 , 我们先来认识几个我们需要知道的成员变量.

             static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;   // 默认初始化可存放元素个数

             static final int MAXIMUM_CAPACITY = 1 << 30;    // 数组内最大存放元素个数

             static final float DEFAULT_LOAD_FACTOR = 0.75f;   //负载因子

             static final int UNTREEIFY_THRESHOLD = 6;    //当链表少于6个的时候, 会再次转为链表

             static final int MIN_TREEIFY_CAPACITY = 64;  // 当一个链表个数达到8,会尝试将其变为红黑树,但是如果这个时候map内元素的总个数 < 64 , 会优先考虑扩容.

       transient Node<K,V>[] table;      // 操作的数组

     

     

     

     a . 直接开始看第三个构造方法.

          1. 首先校验初始化元素个数是否 < 0 ,  true 的话 , 就抛出异常.

          2. 初始化的元素个数是否已经超出了最大值, 如果超出,按最大值来算.

          3. 负载因子是否为 < = 0 或者 不是个Float类型的数字. ( NaN = Not a Number ) , 为true ,抛出异常

          4. 在为 threshold 这个元素赋值的时候, 需要对这个元素重新计算. 要求必须为2的倍数

              当为2的倍数的时候,  key的hash值 % 数组长度 = key的hash值 & (n-1)  . n是数组长度 .

              包括后面扩容时,高位链与低位链的使用.

         

      2. 接下来开始看 put 方法.

            

          

            a . 直接调用了 putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) 方法. 我们先来看一下这个方法的4个参数

                1. hash  . 是将key进行hash运算后的结果. 这里我们可以看到 HashMap的 key 和 value都是允许为空的,如果key为null ,默认存储在数组的第一位.

               

                 2. 这里补充一个点,当数组的Key为一个对象的时候,因为在我们实际的场景中,希望只要对象内属性一样,就默认为同一个.所以我们就需要重写这个对象的 hashCode()方法了.

                 3. 这里的key进行hash()计算,不仅仅是进行了一个hashCode(), 因为是int类型, 这个还将计算出来的结果像右移16位,让高位和地位进行了异或,使高位和地位都参与了运算,在位桶内分布更加均匀.

         b. 第二个参数就是value,我们传输的值

         c . Boolean onlyIfAbsent = false .当这个元素为false的时候,默认key相同时,会对value进行覆盖.为true,则保持第一次的value值不变

         d . evict if false, the table is in creation mode. 默认为true

     

       2. 开始看 putVal方法.

                源码如下 :

      

     

     

    a .  首先判断成员变量 Table 是否为Null ,或者当前数组长度为0 , 那么就需要进行扩容(其实这个时候叫初始化比较合适.)

          进入resize()方法

          初始化进入的时候(见下图代码),

         1.  首先将 成员变量数组 table赋值给 oldTab (当前未扩容的数组)

         2.  oldCap = oldTab.length   (初始化时为 0) 未扩容前数组的长度. 

         3.  threshold , 当我们传输数组初始化长度的时候,这里会将其变为2的幂次方. 比如我们传输的是10 , 那么这里就是16. 如果我们不传输 ,这里就是0 (int类型的默认值) .

         4 . 目前table还是null , 第一个if(old>cap) ,此方法暂时不会进入.

         5. else if ( oldThre > 0) , 如果我们传输的默认大小 10 (变为16了) , 那么现在 newCap = 16

         6 . 此时newThr 还是默认值 0 , 进入 if ( newThr == 0 ) , float ft = 16 * 0.75  , 然后重新赋值给 threshold .

         7. 同时table= new Node[16] ;

         8. 这个时候 table就初始化好了. 后面暂时不用看,因为我们的 oldTable == null

        

     

       a. 接下来原始流程继续往下看.

     

      a . 如上图, 1 的值为当前key所在位桶的索引

      b. 2的值为table在这个索引上存储的头结点Node .

      c. 如果头结点都为Null ,说明当前位桶无元素,直接当前位桶新增一个Node. Node的值为null .  tab[i] = newNode(hash, key, value, null);  

          顺便这里描述一下Node. 这是存在于HashMap的一个静态内部类, 有hash , key , value , next 属性.

     d. 继续往下看,如果头结点不为空.

     

     进入到 else 中.

     

     a . 如果hash值一样(与hashcode()方法有关系), 并且 (k == p.key 或者 key.equals(K) , 这里又用到了equals方法) , 所以当HashMap的key是对象的时候, 我们需要重写这个对象的 hashcode() 方法 和 equasl()方法.

     b . 如果此位桶的头结点 instanceof TreeNode (红黑树类型) , 那就采用 putTreeVal 方法加入到红黑树内.

     c . 接下来进入最后一个else.

     

     a . 说明此位桶上, 链表不为空  且长度不超过 8 , 那就需要开始for循环这个链表覆盖或者新增元素Node了.

     b .  先看for循环内的第一个if . 如果当前阶段的next为null,直接加入new Node() 加入链表的尾端 (多线程情况下,无锁控制,容易出现覆盖Node被覆盖.尾插法) , 如果插入之前的长度 >=7 , 那么这时候就需要尝试将链表改为红黑数了.结束后,跳出for循环.

     c .  第二个if, 如果链表中存在一个元素Key相同,将这个元素赋值给key,跳出for循环.

     d . 继续

     a . 如果e != Null , 说明存在重复的元素.

      这个时候就说到我们之前说的 onlyIfAbsent , 现在默认是 false , 会进行覆盖. 并将oldValue返回. 因为是覆盖,所以不需要 size++ .

     b . 如果为空,说明就是新增了一个元素, 成员变量 size++ .(所以说是非线程安全的)

     c .如果增加之后的size > 临界值 threshold (这个时候就是 16*0.75 = 12) , 这个时候就是真正的扩容了.

     d. 扩容结束,返回null.

     e . 接下来看一下 从 16 扩充到32 的时候,是如何扩容的 (新的数组 位桶为32 , 最大存储容量 = 32 *0.75 = 24) . 再次查看resize方法.

       

    人总得做点什么 ,不是么
  • 相关阅读:
    Codeforces Gym 100571A A. Cursed Query 离线
    codeforces Gym 100500 J. Bye Bye Russia
    codeforces Gym 100500H H. ICPC Quest 水题
    codeforces Gym 100500H A. Potion of Immortality 简单DP
    Codeforces Gym 100500F Problem F. Door Lock 二分
    codeforces Gym 100500C D.Hall of Fame 排序
    spring data jpa 创建方法名进行简单查询
    Spring集成JPA提示Not an managed type
    hibernate配置文件中的catalog属性
    SonarLint插件的安装与使用
  • 原文地址:https://www.cnblogs.com/liweibing/p/12856317.html
Copyright © 2011-2022 走看看