热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

稍稍解读下JDK8的HashMap

首先,源码中上来就有一大段注释,但最重要的就是第一句。大意如下:本map经常用作一个binned(bucketed)hashtable(下面有解释),但是,当bins很大的

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

大意如下:

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

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

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

transient Node[] table;

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

1 static class Node implements Map.Entry {
2     final int hash; //存储hash
3     final K key; //存储key
4     V value; //存储value
5     Node 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[] tab; //指向 HashMap的 table。transient Node[] table;
13     Node 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 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)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 }

 

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

 


推荐阅读
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 本文介绍了Java中Hashtable的clear()方法,该方法用于清除和移除指定Hashtable中的所有键。通过示例程序演示了clear()方法的使用。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 标题: ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • 本文介绍了Java中Currency类的getInstance()方法,该方法用于检索给定货币代码的该货币的实例。文章详细解释了方法的语法、参数、返回值和异常,并提供了一个示例程序来说明该方法的工作原理。 ... [详细]
author-avatar
Edward2502873903
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有