zoukankan      html  css  js  c++  java
  • HashMap put、get方法源码分析

    HashMap.java的实现是面试必问的问题。

    JDK版本

    java version "1.8.0_91"
    Java(TM) SE Runtime Environment (build 1.8.0_91-b15)
    Java HotSpot(TM) 64-Bit Server VM (build 25.91-b15, mixed mode)

    1. HashMap节点的封装

    Node<K, V>

    static class Node<K,V> implements Map.Entry<K,V> {
            final int hash;
            final K key;
            V value;
            Node<K,V> next;
            //.....
    }
        

    hash值算法

    public final int hashCode() {
                return Objects.hashCode(key) ^ Objects.hashCode(value);
            }

    put null的key,计算hash值

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

    ok,重点分析一下扩容

     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         int oldCap = (oldTab == null) ? 0 : oldTab.length;
    13         int oldThr = threshold;
    14         int newCap, newThr = 0;
    15         if (oldCap > 0) {
    16             if (oldCap >= MAXIMUM_CAPACITY) {
    17                 threshold = Integer.MAX_VALUE;
    18                 return oldTab;
    19             }
    20             else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
    21                      oldCap >= DEFAULT_INITIAL_CAPACITY)
    22                 newThr = oldThr << 1; // double threshold
    23         }
    24         else if (oldThr > 0) // initial capacity was placed in threshold
    25             newCap = oldThr;
    26         else {               // zero initial threshold signifies using defaults
    27             newCap = DEFAULT_INITIAL_CAPACITY;
    28             newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    29         }
    30         if (newThr == 0) {
    31             float ft = (float)newCap * loadFactor;
    32             newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
    33                       (int)ft : Integer.MAX_VALUE);
    34         }
    35         threshold = newThr;
    36         @SuppressWarnings({"rawtypes","unchecked"})
    37             Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    38         table = newTab;
    39         if (oldTab != null) {
    40             for (int j = 0; j < oldCap; ++j) {
    41                 Node<K,V> e;
    42                 if ((e = oldTab[j]) != null) {
    43                     oldTab[j] = null;
    44                     if (e.next == null)
    45                         newTab[e.hash & (newCap - 1)] = e;
    46                     else if (e instanceof TreeNode)
    47                         ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
    48                     else { // preserve order
    49                         Node<K,V> loHead = null, loTail = null;
    50                         Node<K,V> hiHead = null, hiTail = null;
    51                         Node<K,V> next;
    52                         do {
    53                             next = e.next;
    54                             if ((e.hash & oldCap) == 0) {
    55                                 if (loTail == null)
    56                                     loHead = e;
    57                                 else
    58                                     loTail.next = e;
    59                                 loTail = e;
    60                             }
    61                             else {
    62                                 if (hiTail == null)
    63                                     hiHead = e;
    64                                 else
    65                                     hiTail.next = e;
    66                                 hiTail = e;
    67                             }
    68                         } while ((e = next) != null);
    69                         if (loTail != null) {
    70                             loTail.next = null;
    71                             newTab[j] = loHead;
    72                         }
    73                         if (hiTail != null) {
    74                             hiTail.next = null;
    75                             newTab[j + oldCap] = hiHead;
    76                         }
    77                     }
    78                 }
    79             }
    80         }
    81         return newTab;
    82     }

    首先说几点

    1. 找tab位置,使用的key.hash() & (capacity - 1)

    注意这里用的是位运算与操作,这里与的是capacity - 1,这样结果就不可能超过capactity - 1。不仅保证了结果正确,性能也提供了很多

    2. 扩容大小为原来的大小左移一位,即扩大为原来的两倍

    扩容主要的移动原来的元素,到新的数组里面

    这里源码使用了比较好的方法,用key的hash值与capacity进行与,注意不是capacity - 1。如果与的结果是0,说明在新数组中的位置为原来的位置,不用变。如果结果不为0,最高为1,即capacty,说明在新数组中的位置为原来位置 + 原来的capacity

    以前面试也被问道过,当时自信满满的胡说。自认为了解的很清楚,这里纠正一下

    1. 封装的节点为Node,不是Entity

    2. 扩容为大小为原来的两倍

    3. 计算在数组位置,不是取余,是采用位操作。hashcode & capactiy - 1,注意这里的 - 1操作

    4. put null的key,这里是可以的,null的hash值为0

    5. key的计算为object.hashcode() ^ (object.hashcode >>> 16)

    先到这

     p.s.https://tech.meituan.com/java-hashmap.html这个写的不错

  • 相关阅读:
    Bash : 冒泡排序
    Azure Load Balancer : 支持 IPv6
    Azure Load Balancer : 简介
    sudo 与输出重定向
    Linux lsof 命令
    Bash : IO 重定向
    LVM : 快照
    2014年全年总结
    使用Kindle4rss推送自己感兴趣的博文
    换SSD硬盘,重装系统,一阵子忙乱
  • 原文地址:https://www.cnblogs.com/luckygxf/p/7465529.html
Copyright © 2011-2022 走看看