zoukankan      html  css  js  c++  java
  • HashMap源码分析

    创建一个最简单的HashMap并打上断点。

    先看看构造方法   另外两个构造方法只是可以自己设置初始容器大小和loadfactor  感兴趣的可以自己看一看

    1     /**
    2      * Constructs an empty {@code HashMap} with the default initial capacity
    3      * (16) and the default load factor (0.75).
    4        DEFAULT_LOAD_FACTOR默认为0.75f   初始容量默认为16
    5      */
    6     public HashMap() {
    7         this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    8     }
    View Code

    接下来进入put方法  put方法核心是putVal 

     1 public V put(K key, V value) {
     2         return putVal(hash(key), key, value, false, true);
     3 }
     4 
     5 //hash方法为计算哈希值  暂时跳过
     6 //接下来是putVal方法
     7 //olnyIfAbsent为false  则改变现有值
     8 //evict为true 则不为确定的模式
     9 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
    10                    boolean evict) {
    11         //创建一个Node数组   P节点  
    12         Node<K,V>[] tab; Node<K,V> p; int n, i;
    13         //一开始为空  resize()方法重新分配内存
    14         if ((tab = table) == null || (n = tab.length) == 0)
    15             n = (tab = resize()).length;
    16         // (n-1)&hash为计算数组中的位置  如果未空直接创建一个新的节点
    17         if ((p = tab[i = (n - 1) & hash]) == null)
    18             tab[i] = newNode(hash, key, value, null);
    19         //不为空则存在冲突  往该位置的链表或者红黑树添加
    20          else {
    21             Node<K,V> e; K k;
    22             //如果当前值的哈希与要加入的哈希相等 并且key也相等  则直接覆盖
    23             if (p.hash == hash &&
    24                 ((k = p.key) == key || (key != null && key.equals(k))))
    25                 e = p;
    26             //否则判断是否是红黑树  是的话 往红黑树添加
    27             else if (p instanceof TreeNode)
    28                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
    29             //此时不是红黑树 则是链表往链表尾部添加  同时判断是否大于阈值8 大于则转换 
    30             //成红黑树 即调用treeifyBin  
    31             //如果没到队尾就发现有哈希值相同 则跳出循环 直接覆盖
    32             else {
    33                 for (int binCount = 0; ; ++binCount) {
    34                     if ((e = p.next) == null) {
    35                         p.next = newNode(hash, key, value, null);
    36                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
    37                             treeifyBin(tab, hash);
    38                         break;
    39                     }
    40                     if (e.hash == hash &&
    41                         ((k = e.key) == key || (key != null && key.equals(k))))
    42                         break;
    43                     p = e;
    44                 }
    45             }
    46             if (e != null) { // existing mapping for key
    47                 V oldValue = e.value;
    48                 if (!onlyIfAbsent || oldValue == null)
    49                     e.value = value;
    50                 afterNodeAccess(e);
    51                 return oldValue;
    52             }
    53         }
    54         ++modCount;
    55         //当数量大于thredshold则扩容
    56         if (++size > threshold)
    57             resize();
    58         afterNodeInsertion(evict);
    59         return null;
    60     }                   

    putVal方法还算容易理解  就是往一个数组加元素  如果冲突后该节点为链表就往链表添加  如果为红黑树则往红黑树添加  哈希相同且key相同则进行替换

    接下来看一下putVal中的 resize()方法

     1 /**
     2      * Initializes or doubles table size.  If null, allocates in
     3      * accord with initial capacity target held in field threshold.
     4      * Otherwise, because we are using power-of-two expansion, the
     5      * elements from each bin must either stay at same index, or move
     6      * with a power of two offset in the new table.
     7      *
     8      * @return the table
     9      */
    10     final Node<K,V>[] resize() {
    11         Node<K,V>[] oldTab = table;
    12         //一开始默认为空
    13         int oldCap = (oldTab == null) ? 0 : oldTab.length;
    14         int oldThr = threshold;
    15         int newCap, newThr = 0;
    16         //大于0则已经初始化
    17         if (oldCap > 0) {
    18             //当前已经达到最大 无需扩充 则threshold为最大  直接返回
    19             if (oldCap >= MAXIMUM_CAPACITY) {
    20                 threshold = Integer.MAX_VALUE;
    21                 return oldTab;
    22             }
    23             //容量和阈值都翻倍
    24             else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
    25                      oldCap >= DEFAULT_INITIAL_CAPACITY)
    26                 newThr = oldThr << 1; // double threshold
    27         }
    28         // 初始容量已存在threshold中
    29         else if (oldThr > 0) // initial capacity was placed in threshold
    30             newCap = oldThr;
    31         //默认构造函数  oldThr为空  则进行初始化  newCap为16   newThr为12
    32         else {               // zero initial threshold signifies using defaults
    33             newCap = DEFAULT_INITIAL_CAPACITY;
    34             newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    35         }
    36         //计算阈值
    37         if (newThr == 0) {
    38             float ft = (float)newCap * loadFactor;
    39             newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
    40                       (int)ft : Integer.MAX_VALUE);
    41         }
    42         //threshold在这里进行赋值
    43         threshold = newThr;
    44         @SuppressWarnings({"rawtypes","unchecked"})
    45         Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    46         table = newTab;
    47         //之前已经初始化  现在开始扩容
    48         if (oldTab != null) {
    49             //复制元素,重新进行hash
    50             for (int j = 0; j < oldCap; ++j) {
    51                 Node<K,V> e;
    52                 if ((e = oldTab[j]) != null) {
    53                     oldTab[j] = null;
    54                     if (e.next == null)
    55                         newTab[e.hash & (newCap - 1)] = e;
    56                     else if (e instanceof TreeNode)
    57                         ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
    58                     else { // preserve order
    59                         Node<K,V> loHead = null, loTail = null;
    60                         Node<K,V> hiHead = null, hiTail = null;
    61                         Node<K,V> next;
    62                         do {
    63                             next = e.next;
    64                             if ((e.hash & oldCap) == 0) {
    65                                 if (loTail == null)
    66                                     loHead = e;
    67                                 else
    68                                     loTail.next = e;
    69                                 loTail = e;
    70                             }
    71                             else {
    72                                 if (hiTail == null)
    73                                     hiHead = e;
    74                                 else
    75                                     hiTail.next = e;
    76                                 hiTail = e;
    77                             }
    78                         } while ((e = next) != null);
    79                         if (loTail != null) {
    80                             loTail.next = null;
    81                             newTab[j] = loHead;
    82                         }
    83                         if (hiTail != null) {
    84                             hiTail.next = null;
    85                             newTab[j + oldCap] = hiHead;
    86                         }
    87                     }
    88                 }
    89             }
    90         }
    91         return newTab;
    92     }

    由此可见,扩容较为复杂  扩容机制为原来的两倍   同时会遍历每个元素重新hash找位置 比较耗时,应尽量避免。

    get方法较为简单,就不分析了  大致就是hash  然后找数组上是否存在  存在则判断是否有红黑树  链表等等 

    小总结:数组大小n总是2的整数次幂,因此计算下标时直接( hash & n-1),这样的好处就是可以直接取代取模运算,提高计算速度。分配内存初始化通过resize()方法 ,主要时初始化的时候和put方法超过阈值的时候扩容,因此最好是在构造函数就进行初始化。哈希冲突时 转为链表存储,如果链表的长度大于8则转化为红黑树。

  • 相关阅读:
    MQTT的编译和安装(mosquitto)
    四、固件分析-固件逆向
    三、固件分析-固件提取
    二、硬件分析-电路分析
    一、硬件分析
    gdb调试
    AES加密中遇到的坑
    多线程中快速定位段错误位置
    理想的数据加密流程
    Centos进入单用户修改root密码
  • 原文地址:https://www.cnblogs.com/helloDuo/p/9544678.html
Copyright © 2011-2022 走看看