使用阿里云代码规范插件扫描后出现以下提示:

hashmap should set a size when initalizing,即hashmap应该在初始化时设置一个大小

在网上搜到一篇讲解(https://www.cnblogs.com/coderxuyang/p/3718856.html),如下:

在元素的装载数量明确的时候HashMap的大小应该如何选择。

今天看到美团招聘给出了一道小题目,关于HashMap的性能问题。问题如下:

java hashmap,如果确定只装载100个元素,new HashMap(?)多少是最佳的,why?

要回答这个问题,首先得知道影响HashMap性能的参数有哪些。咱们翻翻JDK。

在JDK6中是这么描述的:

HashMap的实例有两个参数影响其性能:初始容量和加载因子。

首先我们来看初始容量和加载因子的定义。

容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。

加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。

当哈希表中条目的数目超过 容量乘加载因子 的时候,则要对该哈希表进行rehash操作,从而哈希表将具有大约两倍的桶数。(以上摘自JDK6)

HashMap默认的加载因子是0.75 .它在时间和空间成本上寻求了一种折中。

回到本文的问题。根据JDK中的描述,如果这个只装载100个元素的HashMap没有特殊的用途,那么为了在时间和空间上达到最佳性能,HashMap的初始容量可以设为

100/0.75 = 133.33。为了防止rehash,向上取整,为134。

但是还有另外一个问题,就是hash碰撞的问题。如果我们将HashMap的容量设置为134,那么如何保证其中的哈希碰撞会比较少呢?

除非重写hashcode()方法,否则,似乎没有办法保证。

那么这里不得不提HashMap如何为元素选择下标的方法了。

    static int indexFor(int h, int length) {
return h & (length-1);
}

其中h为key哈希后得到的值,length为哈希表的长度。

134-1 = 128 + 6 -1;

那么 length-1的二进制值的最后3位为101;

假设这100个装载的元素中他们的key在哈希后有得到两个值(h),他们的二进制值除了低3位之外都相同,而第一个值的低3位为011,第二个值的低3位为001;

这时候进行java的&预算,011 & 101 = 001 ;001 & 101 = 001;

他们的值相等了,那么这个时候就会发生哈希碰撞。

除此之外还有一个更加严重的问题,由于在101中第二位是0,那么,无论我们的key在哈希运算之后得到的值h是什么,那么在&运算之后,得到的结果的倒数第二位均为0;

因此,对于hash表所有下标的二进制的值而言,只要低位第二位的值为1,(例如0010,0011,0111,1111)那么这个下标所代表的桶将一直是空的,因为代码中的&运算的结果永远不会产生低位第二位为1的值。这就大大地浪费了空间,同时还增加了哈希碰撞的概率。这无疑会降低HashMap的效率。

那么如何才能减少这种浪费呢?最佳的方法当然是让length-1的二进制值全部位均为1.那么length的值是多少合适呢?

没错,length=2^n。

只要将hash表的长度设为2的N次方,那么,所有的哈希桶均有被使用的可能。

再次回到美团提出的问题,与134最靠近的2^n无疑是128。

如果只修改HashMap的长度而不修改HashMap的加载因子的话,HashMap会进行rehash操作,这是一个代价很大的操作,所以不可取。

那么应该选择的就应该是256。

而由于空间加大和有效利用哈希桶,这时的哈希碰撞将大大降低,因此HashMap的读取效率会比较高。

所以,最后结论就是:HashMap的大小应该设置为256。

结果的补充:其实在Java中,无论你的HashMap(x)中的x设置为多少,HashMap的大小都是2^n。2^n是大于x的第一个数。因为HashMap的初始化代码中有以下这行代码:

 int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;

但是这就带来了一个问题,如果x=100,那么HashMap的初始大小应该是128.但是100/128=0.78,已经超过默认加载因子(0.75)的大小了。因此会resize一次,变成256。所以最好的结果还是256。

最后发个参考链接:http://www.iteye.com/topic/539465

另,总结StringBuffer、ArrayList、HashMap的扩容:

StringBuffer:内部实现是一个字符数组。初始默认大小为16,当然也可以在其构造方法中进行设置。当新添加字符或字符串时,发现数组容量不够。这个时候就需要使用Array.copyOf()方法进行扩充。扩充的新的数组大小等于,(原始容量*2+2)和(数组实际字符个数+新增的字符长度)之间的较大值。

ArrayList:内部实现是一个Object的数组。初始默认大小为0,当然也可以在其构造方法中设置。当添加一个Object时,默认扩充数组容量为10。然后每次扩充的新的数组大小等于,(原始容量*3/2)和(数组的长度+1)之间的较大值。根据每次增加一个Object,可得该情况每次扩充的固定大小为3/2。当初始大小为手动设置的时候,每次扩充的新的数组大小等于,(原始容量*3/2)和(数组的长度+1)之间的较大值。

HashMap:内部实现是一个Entry的数组,默认大小是空的数组。初始化的容量是16,加载因子是3/4(当数组元素数量大于总容量的加载因子的时候,扩充数组)。当默认不是空的数组时,当达到加载因子的比例的时候,每次扩充初始容量的2倍

关于HashMap初始化容量问题的更多相关文章

  1. HashMap初始化容量过程

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

  2. hashmap 为什么初始化容量是2的幂次方

    个人理解 做下记录,不正确的地方望不吝赐教 这是hashmap初始化容量时候 对容量大小做的处理,保证初始化容量为最近的2的幂次方(JDK1.8) static final int tableSize ...

  3. 阿里巴巴Java开发手册建议创建HashMap时设置初始化容量,但是多少合适呢?

    集合是Java开发日常开发中经常会使用到的,而作为一种典型的K-V结构的数据结构,HashMap对于Java开发者一定不陌生. 关于HashMap,很多人都对他有一些基本的了解,比如他和hashtab ...

  4. 为什么要指定HashMap的容量?HashMap指定容量初始化后,底层Hash数组已经被分配内存了吗?

    为什么要指定HashMap的容量? 首先创建HashMap时,指定容量比如1024后,并不是HashMap的size不是1024,而是0,插入多少元素,size就是多少: 然后如果不指定HashMap ...

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

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

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

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

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

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

  8. Java 中 HashMap 初始化时赋值

      1.HashMap 初始化的文艺写法 HashMap 是一种常用的数据结构,一般用来做数据字典或者 Hash 查找的容器.普通青年一般会这么初始化:HashMap<String, Strin ...

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

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

随机推荐

  1. Kotlin【简介】Android开发 配置 扩展

    重要资源 官方资源:官网 . 官网源码 .官网文档 . 在线 IDE .GitHub 中文资源:中文官网 .中文文档  离线文档:PDF 文件 . PDF 文件 GitBook 版 .ePUB 文件  ...

  2. javascript学习笔记------概念相关

    javascript中的函数.对象 1. 在javascript中,函数是被当成一种数据类型,它可以被存储在一个变量.数组.对象中,可以被当作参数传递到另一个函数中. 函数就像是字符串和数字这样的的数 ...

  3. JavaScript:避免代码的重复执行

    我喜欢到一些大型网站上去翻阅它们的原代码,期望能找到一些可以应用到自己的代码中的模式,或发现一些之前从未听说过的工具和技巧.可是,在我查看这些大型网站的源代码时,经常会发现一个问题,那就是重复的代码执 ...

  4. linux 处理键盘 鼠标事件

    Linux下鼠标和键盘的模拟控制,也就是为手势和语音控制鼠标和键盘部分服务的. 有关于本系统构建的文章结构都会由三个部分来组织,一是该功能模块的介绍和在Linux下简单应用程序的实现:二是将该功能模块 ...

  5. Java Web学习总结-文件下载

    参考资料:https://www.cnblogs.com/xdp-gacl/p/4200090.html 在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件 ...

  6. 从AIDL开始谈Android进程间Binder通信机制

    转自: http://tech.cnnetsec.com/585.html 本文首先概述了Android的进程间通信的Binder机制,然后结合一个AIDL的例子,对Binder机制进行了解析. 概述 ...

  7. 使用nmonchart把.nmon文件转换成html

    转载:https://blog.csdn.net/zd470015321/article/details/68923280 我的环境 :centos6.6 下载地址 nmon: http://nmon ...

  8. C#基础视频教程4.1 如何编写简单的计算器

    要学会基本的窗体控件(button,textbox,label这种,以及他们的重要属性),每个控件的命名要符合规范(例如一般文本框都会重命名txtXXXXX,按钮都会重命名btnXXXXX)   为了 ...

  9. Effective java读书札记第一条之 考虑用静态工厂方法取代构造器

    对于类而言,为了让client获取它自身的一个实例,最经常使用的方法就是提供一个共同拥有的构造器. 另一种放你发,也应该子每一个程序猿的工具箱中占有一席之地.类能够提供一个共同拥有的静态 工厂方法.它 ...

  10. Drawing text

    We begin with drawing some Unicode text on the client area of a window. #!/usr/bin/python # -*- codi ...