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

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. MapReduce 编程模型概述

    MapReduce 编程模型给出了其分布式编程方法,共分 5 个步骤:1) 迭代(iteration).遍历输入数据, 并将之解析成 key/value 对.2) 将输入 key/value 对映射( ...

  2. Ubuntu 中/etc/resolv.conf 文件修改丢失的解决方案

    方法一 1.需要创建一个文件/etc/resolvconf/resolv.conf.d/tail sudo vi /etc/resolvconf/resolv.conf.d/tail 2.在该文件中写 ...

  3. import MySQLdb UserWarning

    Finished processing dependencies for MySQL-python==1.2.5 ╭─haoke@haokedeMBP ~/ProgramFiles/MySQL-pyt ...

  4. 【Python】nvshens按目录批量下载图片爬虫1.00(单线程版)

    # nvshens按目录批量下载图片爬虫1.00(单线程版) from bs4 import BeautifulSoup import requests import datetime import ...

  5. Android多点触控技术,实现对图片的放大缩小平移,惯性滑动等功能

    首先推荐一下鸿洋大大的打造个性的图片预览与多点触控视频教程,这套教程教我们一步一步实现了多点触控实现对图片的平移和缩放的功能.这篇文章我将在鸿洋大大的基础之上做了一些扩展功能: 1.图片的惯性滑动 2 ...

  6. SQL Server 之 与 OVER() 函数

    在SQL SERVER 2005/2008支持两种排名开窗函数和聚集开窗函数. 一. OVER() 函数 语法结构:OVER( [ PARTITION BY ... ] [ ORDER BY ... ...

  7. MyEclipse发布按钮无效的办法

    删除存放您MyEclipse项目的目录下的“/.metadata/.plugins/org.eclipse.core.runtime/.settings/com.genuitec.eclipse.as ...

  8. 总想自己动动手系列·3·微信公众号和外网服务交互之通过TOKEN验证(准备篇·1)

    一.准备工作 (1)准备一个微信公众号(对私的订阅号或者对公的服务号). (2)准备一台部署了web应用,并且已经发布出去的Linux服务器(需要说明的是:微信公众号强烈建议使用80端口,使用其他自定 ...

  9. CKEditor与CKFinder学习--CKFinder源代码改动自己定义上传文件名称

    CKFinder的系列文章到眼下应该说基本能够满足开发需求了,只是另一个小细节,CKFinder默认上传的文件名称和源文件名称一致,假设文件名称反复会自己主动加入编号"(1)"&q ...

  10. oracle的check约束

    check约束是指检查性约束,使用check约束时.将对输入的每个数据进行检查,仅仅有符合条件的记录才会被保存到表中,从而保证了数据的有效性和完整性.      check约束既有下面的四个特点: 在 ...