zoukankan      html  css  js  c++  java
  • 稍稍解读下JDK8的HashMap

    首先,源码中上来就有一大段注释,但最重要的就是第一句。

    大意如下:

      本map经常用作一个 binned (bucketed) hash table (下面有解释),但是,当bins很大的时候,它们会被转换成 bins of TreeNodes,每个bin的结构类似于TreeMap。

    先解释下这里的bin,直译是容器、箱子。其实这里你可以认为它代表一个hash值对应的位置!

    HashMap使用 HashMap.Node<K, V> 来存储具体的key和value,又用 Node[] 来存储Node。

    transient Node<K,V>[] table;

    要注意,这个Node实际上是链表结构的:

    1 static class Node<K,V> implements Map.Entry<K,V> {
    2     final int hash; //存储hash
    3     final K key; //存储key
    4     V value; //存储value
    5     Node<K,V> next; //存储下一个Node,所以是链表结构
    6 
    7     //其他略
    8 }

    如果不考虑hash碰撞,每当map.put(k, v)时,实际上是构建了一个Node,再将Node放到Node数组(也就是table)中。

    当然位置是经过计算的:

    table[(n-1) & hash] = newNode; //注意,不是数组的最后一个位置

    但是,难免出现hash碰撞,也就是一个hash值有多个对象(或者多个对象的hash值一致)!这时候该怎么做?

    其实在JDK7及之前,都是直接使用Node.next来安置的,也就是说,将新的对象追加到链表的尾部。

    但是,JDK8有所改变,就是本文开头那句话讲述的内容。

    直接看源码好了(对JDK的源码是恨得咬牙切齿,因为各种不人性化的东西):

     1 /**
     2     * Implements Map.put and related methods
     3     *
     4     * @param hash k的哈希值
     5     * @param key k
     6     * @param value 要存储的v
     7     * @param onlyIfAbsent true则不修改现有的值
     8     * @param evict false 则 table是在creation mode。
     9     * @return 前值,或null(如果没有前值)
    10     */
    11 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
    12     Node<K,V>[] tab; //指向 HashMap的 table。transient Node<K,V>[] table;
    13     Node<K,V> p;  //用于指向 table 中的Node对象,如果该对象还有next,则最终会指向next。--当成prev比较合适,因为要插入的对象最终会放在它后面!
    14     int n, i; //n是table的长度;i是table中的索引。
    15     if ((tab = table) == null || (n = tab.length) == 0) //如果table为空(续)
    16         n = (tab = resize()).length; //则重新分配
    17 
    18     if ((p = tab[i = (n - 1) & hash]) == null) //(n-1)&hash 用于计算新对象的索引位置,如果该位置上的对象为null(续)
    19         tab[i] = newNode(hash, key, value, null); //直接创建Node,放到该位置上!
    20     else { //如果位置上已有对象(续)
    21         Node<K,V> e; 
    22         K k;
    23         if (p.hash == hash &&
    24             ((k = p.key) == key || (key != null && key.equals(k)))) //如果hash和key还一样(续)
    25             e = p; 
    26         else if (p instanceof TreeNode) //如果hash和key不一样,就判断是否是TreeNode对象(续)
    27             e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); //是TreeNode对象的话,就调用putTreeVal(..)!
    28         else { //到了这里,说明既不是同一个对象,也不是TreeNode,(续)
    29             for (int binCount = 0; ; ++binCount) { //那就来个循环
    30                 if ((e = p.next) == null) { //如果当前Node没有next
    31                     p.next = newNode(hash, key, value, null); //新建Node,并添加到当前Node.next
    32                     if (binCount >= TREEIFY_THRESHOLD - 1) //再判断链表的长度是否超出限制
    33                         treeifyBin(tab, hash);//超出指定长度,则将该链表转成树!
    34                     break;
    35                 }
    36                 if (e.hash == hash &&
    37                     ((k = e.key) == key || (key != null && key.equals(k)))) //这里是判断当前链表中的Node存储的对象,与要存储的对象是否同一个
    38                     break;
    39                 p = e; //沿着链表前进一个位置。等同于p=p.next
    40             }
    41         }
    42         if (e != null) { // existing mapping for key
    43             V oldValue = e.value;
    44             if (!onlyIfAbsent || oldValue == null)
    45                 e.value = value;
    46             afterNodeAccess(e);
    47             return oldValue;
    48         }
    49     }
    50     ++modCount;
    51     if (++size > threshold)
    52         resize();
    53     afterNodeInsertion(evict);
    54     return null;
    55 }

    源码注释不太方便啊,难道要来个流程图?还是算鸟,赶紧睡觉啦(*^_^*)

  • 相关阅读:
    Python入门
    实现QQ、微信、新浪微博和百度第三方登录(Android Studio)
    Android 微信第三方登录
    Javascript获取随机数
    JavaScript for循环 闭包 【转】
    JavaScript中数组的增删改查
    【网络基础系列一】客户/服务器模型
    jQuery选择器
    局部代码块
    接口中定义变量
  • 原文地址:https://www.cnblogs.com/larryzeal/p/8713821.html
Copyright © 2011-2022 走看看