原文链接:https://www.changxuan.top/?p=1208


前两天,我在一位同学提交中看到了下面这样的一行代码,让我很是惊讶。

Map<String, String> temp = new HashMap<>(6);

我给他说,你这样实例化 Map 对象不好用,他不服气。我说小朋友:如果想指定 HashMap 对象的容量得用2的N次方。他说你这也没用。我说,我这个有用,这样才能充分利用分配的内存空间。他非和我试试,我说可以,不过得先一起看看源码。

什么是HashMap?

在弄懂标题的问题之前,首先需要清楚 HashMap 的概念。HashMap 是基于哈希表的 Map 接口的实现,线程不安全,且不保证映射顺序。

HashMap 存储数据依赖的是数组和[链表|红黑树],具体链表和红黑树之间如何转换的细节此文不做详细介绍。而本文开头提到的实例化容量大小指的则是数组的大小。

如何计算元素在数组中所对应的下标?

首先计算元素的哈希值,方法如下:

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

为什么不直接使用 key.hashCode()的值,我们后面会提到。

计算出来哈希值后,由于数组容量相对来说较小肯定不能直接使用哈希值当作索引值。所以需要使用哈希值对数组长度减一后的值取模。不过在在 HashMap 中可不是直接使用 % 运算符来操作的。为了提高效率,采用的是与运算的方式,代码如下:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
      // n 为数组容量, (n-1) & hash 则是计算索引值
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
          ... ...
        }
}

既然清楚了计算元算在数组中所对应下标的方法,那么证明为什么实例化 HashMap 对象的容量要使用2的N次方就简单多了。

假如初始容量为2的3次方数字8,当哈希值与容量大小减一的值进行与运算时可以保证结果比较均匀的分布在数组上。

  10100101 11000100 00100101
& 00000000 00000000 00000111 // 7
----------------------------------
  00000000 00000000 00000101 // 结果可以是[0,7]中的任一数字

如果初始容量为6,那么出现哈希冲突的几率就会增加了。

  10100101 11000100 00100101
& 00000000 00000000 00000101 // 5 
----------------------------------
  00000000 00000000 00000101 // 5
  
  10100101 11000100 00100111
& 00000000 00000000 00000101 // 5 
----------------------------------
  00000000 00000000 00000101 // 5

如果下面的值低位全是1,那么上面的这次哈希冲突则可以避免。那么你想想,假如指定的容量大小为5又会怎么样呢?其实2的N次方数字-1的二进制形式这个特性在好多地方会很好用,可以在小本本记上。

哦,前面说为什么计算出来的散列值需要再让高16位和低十六位做异或运算,主要是让参与与运算的位同时具有高位和低位的特征,来减少哈希碰撞次数。

小朋友,还试不试啦!

为什么 HashMap 的容量大小要设置为2的N次方?的更多相关文章

  1. HashMap的容量大小增长原理(JDK1.6/1.7/1.8)

    . 前言 HashMap的容量大小会根据其存储数据的数量多少而自动扩充,即当HashMap存储数据的数量到达一个阈值(threshold)时,再往里面增加数据,便可能会扩充HashMap的容量. 可能 ...

  2. 关于HashMap初始化容量问题

    使用阿里云代码规范插件扫描后出现以下提示: hashmap should set a size when initalizing,即hashmap应该在初始化时设置一个大小 在网上搜到一篇讲解(htt ...

  3. 我说我了解集合类,面试官竟然问我为啥HashMap的负载因子不设置成1!?

    在Java基础中,集合类是很关键的一块知识点,也是日常开发的时候经常会用到的.比如List.Map这些在代码中也是很常见的. 个人认为,关于HashMap的实现,JDK的工程师其实是做了很多优化的,要 ...

  4. HashMap初始化容量过程

    集合是Java开发日常开发中经常会使用到的,而作为一种典型的K-V结构的数据结构,HashMap对于Java开发者一定不陌生.在日常开发中,我们经常会像如下方式以下创建一个HashMap: Map&l ...

  5. 我说HashMap初始容量是16,面试官让我回去等通知

    众所周知HashMap是工作和面试中最常遇到的数据类型,但很多人对HashMap的知识止步于会用的程度,对它的底层实现原理一知半解,了解过很多HashMap的知识点,却都是散乱不成体系,今天一灯带你一 ...

  6. 【转载】VMware虚拟机修改硬盘容量大小

    很多人在安装虚拟机系统的时候,为了节省硬盘空间,把硬盘容量设置得较小,可是后来发现硬盘容量不够用了.在VMware中又不能直接修改虚拟机的硬盘容量大小,或者重建虚拟机系统,非常麻烦. 其实在VMwar ...

  7. jdk1.8 HashMap底层数据结构:深入解析为什么jdk1.8 HashMap的容量一定要是2的n次幂

    前言 1.本文根据jdk1.8源码来分析HashMap的容量取值问题: 2.本文有做 jdk1.8 HashMap.resize()扩容方法的源码解析:见下文“一.3.扩容:同样需要保证扩容后的容量是 ...

  8. 为什么jdk1.8 HashMap的容量一定要是2的n次幂

    一.jdk1.8中,对“HashMap的容量一定要是2的n次幂”做了严格控制 1.默认初始容量: [Java] 纯文本查看 复制代码 ? 1 2 3 4 /**  * The default init ...

  9. 根据屏幕大小动态设置字体rem

    1.根据屏幕大小动态设置字体rem var docEl = document.documentElement, //当设备的方向变化(设备横向持或纵向持)此事件被触发.绑定此事件时, //注意现在当浏 ...

随机推荐

  1. 对JVM的一个基础了解

    1.JVM范围 2.JVM和class文件 (1).JVM和Java语言无关,JVM是一种规范,任何语言只要能编译成class文件格式都能在JVM上运行 3.class文件格式 (1).class文件 ...

  2. elasticsearch 使用同义词

    elasticsearch 使用同义词 使用环境 elasticsearch5.1.1 kibana5.1.1 同义词插件5.1.1 安装插件 下载对应的elasticsearch-analysis- ...

  3. yii2-imagine的使用

    <?php /** * 图片常用处理 * * 需要 yii/yii2-imagine 的支持 * php composer.phar require --prefer-dist yiisoft/ ...

  4. 配置jdb

    目录 注:1)查看当前Linux系统是否已经安装java 1.把jdk文件的压缩包拖入虚拟机 2.找到刚刚拖拽的文件 3.在usr/local下创建jdk的文件夹 4.解压jdk的文件,并存放在刚刚创 ...

  5. 10、Spring Boot分布式

    1.分布式简介  2.Zookeeper和Dubbo  3.zookeeper (1).zookeeper安装 官方文档:https://hub.docker.com/_/zookeeper?tab= ...

  6. V-指令,细节补充

    v-for遍历对象的时候 v-for=" (item) in person" 这里item是person对象里面的value值 而v-for=" (item,key) i ...

  7. C语言位运算符详解

    原文链接:https://www.cnblogs.com/911/archive/2008/05/20/1203477.html 位运算是指按二进制进行的运算.在系统软件中,常常需要处理二进制位的问题 ...

  8. 【2014广州市选day1】JZOJ2020年9月12日提高B组T2 导弹拦截

    [2014广州市选day1]JZOJ2020年9月12日提高B组T2 导弹拦截 题目 Description 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统 V1.0.但是这种导弹拦截系统有一个缺 ...

  9. .Net编码规范整理

    前言 此处只是整理并记录下.Net开发规范以便加深编码规范.一个好的编程规范可以让自己程序可读性,让自己编码更规范,分为两部分:通用规范..Net开发规范. 微软通用编程规范 明确性和一致性 库的使用 ...

  10. mysql索引原理以及优化

    一.常见查找算法: 1.顺序查找: 最基础的查找方法,对比每一个元素进行查找.在数据量很大的时候效率相当的慢. 数据结构:有序或者无需的队列 时间复杂度:O(n) 2.二分查找: 二分查找首先要求数组 ...