为什么java Hashmap 中的加载因子是默认为0.75_java中0.75-程序员宅基地

技术标签: 学习  java基础  java  源码  hashmap  数据结构  

转自:http://www.jianshu.com/p/dff8f4641814

前几天在一个群里看到有人讨论hashmap中的加载因子为什么是默认0.75。

HashMap源码中的加载因子

static final float DEFAULT_LOAD_FACTOR = 0.75f;

当时想到的是应该是“哈希冲突”和“空间利用率”矛盾的一个折衷。
跟数据结构要么查询快要么插入快一个道理,hashmap就是一个插入慢、查询快的数据结构。

加载因子是表示Hsah表中元素的填满的程度。
加载因子越大,填满的元素越多,空间利用率越高,但冲突的机会加大了。
反之,加载因子越小,填满的元素越少,冲突的机会减小,但空间浪费多了。

冲突的机会越大,则查找的成本越高。反之,查找的成本越小。

因此,必须在 "冲突的机会"与"空间利用率"之间寻找一种平衡与折衷。


但是为什么一定是0.75?而不是0.8,0.6

本着不嫌事大的精神继续深挖,在此之前先简单补充点本文需要的基础知识:

1.冲突定义:假设哈希表的地址集为[0,n),冲突是指由关键字得到的哈希地址为j(0<=j<=n-1)的位置上已经有记录。在关键字得到的哈希地址上已经有记录,那么就称之为冲突

2.处理冲突:就是为该关键字的记录扎到另一个“空”的哈希地址。即在处理哈希地址的冲突时,若得到的另一个哈希地址H1仍然发生冲突,则再求下一个地址H2,若H2仍然冲突,再求的H3,直至Hk不发生冲突为止,则Hk为记录在表中的地址。


处理冲突的几种方法:

一、 开放定址法

Hi=(H(key) + di) MOD m i=1,2,...k(k<=m-1)其中H(key)为哈希函数;m为哈希表表长;di为增量序列。

开放定址法根据步长不同可以分为3种:

1)线性探查法(Linear Probing):di=1,2,3,...,m-1
  简单地说就是以当前冲突位置为起点,步长为1循环查找,直到找到一个空的位置就把元素插进去,循环完了都找不到说明容器满了。就像你去一条街上的店里吃饭,问了第一家被告知满座,然后挨着一家家去问是否有位置一样。

2)线性补偿探测法:di=Q 下一个位置满足 Hi=(H(key) + Q) mod m i=1,2,...k(k<=m-1) ,要求 Q 与 m 是互质的,以便能探测到哈希表中的所有单元。
继续用上面的例子,现在你不是挨着一家家去问了,拿出计算器算了一下,然后隔Q家问一次有没有位置。

3)伪随机探测再散列:di=伪随机数序列。还是那个例子,这是完全根据心情去选一家店来问了

缺点:

  • 这种方法建立起来的hash表当冲突多的时候数据容易堆聚在一起,这时候对查找不友好;
  • 删除结点不能简单地将被删结 点的空间置为空,否则将截断在它之后填人散列表的同义词结点的查找路径。因此在 用开放地址法处理冲突的散列表上执行删除操作,只能在被删结点上做删除标记,而不能真正删除结点
  • 当空间满了,还要建立一个溢出表来存多出来的元素。

二、再哈希法

Hi = RHi(key),i=1,2,...k
RHi均是不同的哈希函数,即在同义词产生地址冲突时计算另一个哈希函数地址,直到不发生冲突为止。这种方法不易产生聚集,但是增加了计算时间。

缺点:增加了计算时间。

三、建立一个公共溢出区

假设哈希函数的值域为[0,m-1],则设向量HashTable[0...m-1]为基本表,每个分量存放一个记录,另设立向量OverTable[0....v]为溢出表。所有关键字和基本表中关键字为同义词的记录,不管他们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入溢出表。

简单地说就是搞个新表存冲突的元素。

四、链地址法(拉链法)

将所有关键字为同义词的记录存储在同一线性链表中,也就是把冲突位置的元素构造成链表。

拉链法的优点:

  • 拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;
  • 由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;
  • 在用拉链法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。

拉链法的缺点:

  • 指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间,而若将节省的指针空间用来扩大散列表的规模,可使装填因子变小,这又减少了开放定址法中的冲突,从而提高平均查找速度

Java中HashMap的数据结构

HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。


HashMap数据结构,来源于网络

看图就可以知道Java中的hashMap使用了拉链法处理冲突。
HashMap有一个初始容量大小,默认是16

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

为了减少冲突的概率,当hashMap的数组长度到了一个临界值就会触发扩容,把所有元素rehash再放到扩容后的容器中,这是一个非常耗时的操作。

而这个临界值由【加载因子】和当前容器的容量大小来确定:DEFAULT_INITIAL_CAPACITY*DEFAULT_LOAD_FACTOR ,即默认情况下是16x0.75=12时,就会触发扩容操作。

所以使用hash容器时尽量预估自己的数据量来设置初始值。具体代码实现自行去研究HashMap的源码。

基础知识补充完毕,回到正题,为什么加载因子要默认是0.75?
从hashmap源码注释里找到了这一段

Ideally, under random hashCodes, the frequency of

  • nodes in bins follows a Poisson distribution
  • (http://en.wikipedia.org/wiki/Poisson_distribution) with a
  • parameter of about 0.5 on average for the default resizing
  • threshold of 0.75, although with a large variance because of
  • resizing granularity. Ignoring variance, the expected
  • occurrences of list size k are (exp(-0.5) * pow(0.5, k) /
  • factorial(k)). The first values are:
  • 0: 0.60653066
  • 1: 0.30326533
  • 2: 0.07581633
  • 3: 0.01263606
  • 4: 0.00157952
  • 5: 0.00015795
  • 6: 0.00001316
  • 7: 0.00000094
  • 8: 0.00000006
  • more: less than 1 in ten million

注意wiki链接中的关键字:Poisson_distribution
泊淞分布啊

简单翻译一下就是在理想情况下,使用随机哈希码,节点出现的频率在hash桶中遵循泊松分布,同时给出了桶中元素个数和概率的对照表。

从上面的表中可以看到当桶中元素到达8个的时候,概率已经变得非常小,也就是说用0.75作为加载因子,每个碰撞位置的链表长度超过8个是几乎不可能的。

好了,再深挖就要挖到统计学那边去了,就此打住,重申一下使用hash容器请尽量指定初始容量,且是2的幂次方。

关于泊淞分布的知识请看

http://www.ruanyifeng.com/blog/2015/06/poisson-distribution.html#comment-356111

欢迎讨论,这个周末的任务完成了,可以放心去浪了─=≡Σ((( つ•̀ω•́)つ超人

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/hcmony/article/details/56494527

智能推荐

深入分析MPU6050在ESP32的实现流程_esp32 mpu6050-程序员宅基地

文章浏览阅读1k次。概念与操作描述一种用于在集成电路之间进行数据传输的串行通信协议。主设备(Master)发起通信并控制整个数据传输过程的设备。从设备(Slave)被动响应主设备请求的设备。数据线(SDA)双向的数据线,用于传输数据。时钟线(SCL)主设备通过时钟线控制数据的传输速率。地址每个从设备在总线上具有唯一的地址,用于识别设备。读写操作主设备向从设备发送读取或写入命令,用于读取从设备的数据或向从设备写入数据。应答(ACK/NACK)_esp32 mpu6050

畅购商城第五天_商城系统 es商品搜索排序-程序员宅基地

文章浏览阅读736次。第5章 商品搜索学习目标Elasticsearch安装docker安装Elasticsearch系统参数问题跨域操作IK分词器配置Kibana的使用->DSL语句Kibana->DSL语句操作->ElasticsearchES导入商品搜索数据Sku数据导入到ElasticsearchMap数据类型->Object关键词搜索->能够实现搜索流程代码的编写分类统计搜索1. Elasticsearch 安装我们之前已经使_商城系统 es商品搜索排序

(BDCI-CCF)出租车发票识别_出租车发票识别ccf-程序员宅基地

文章浏览阅读3.1k次,点赞4次,收藏16次。参考文章:​​​​​​百度AI攻略:出租车票识别_才能我浪费的博客-程序员宅基地附完整python源码)基于tensorflow、opencv的入门案例_发票识别一:关键区域定位_小白来搬家-程序员宅基地_python发票识别注:感谢一起完成项目的队友们大赛官网:出租车发票识别 Competitions - DataFountain一、赛题说明1.赛题背景出租车发票在日常财务发票报销中较为常见,由于这类发票样式丰富,区域性特点明显,并且包含大量模糊字迹和错位字迹,因此准确的定位发票_出租车发票识别ccf

c++协程3 (boost::coroutine)_c++ boost什么版本支持协程-程序员宅基地

文章浏览阅读2.6k次。#include #include #include #include using boost::coroutines::coroutine;void cooperative(coroutine>::pull_type &source){ auto args = source.get(); std::cout (args) (args) << '\n'; source()_c++ boost什么版本支持协程

数据结构与算法 - 线性表(完整代码)_线性表完整程序代码-程序员宅基地

文章浏览阅读5.3k次,点赞7次,收藏32次。样例输入:3 //输入一个数a,后面将输入a个数据元素8 9 3 //a个数据元素,依次插入尾结点后。形成单链表结点序列:8,9,33 //输入一个数b,后面将再输入b个数据元素10 89 22 //b个数据元素,依次插入0号结点后。形成单链表结点序列:8,22,89,10,9,389 //删除一个值为89的结点1 //删除1号结点样例输出:8 10 9 3#inc..._线性表完整程序代码

fastjson自动过滤null值解决方案_fastjson parseobject 过滤值为null的字段-程序员宅基地

文章浏览阅读2.7k次,点赞2次,收藏2次。fastjson转换字符串自动过滤value为null的问题背景:使用 JSON.toJSONString(Object object)的时候发现打印出来的字符串少了几个字段,以为代码没走相应的逻辑,发现少的都是value为null的字段,发现是fastjson自动把value为null的字段过滤了。解决办法:设置SerializerFeature序列化属性 SerializerFeatu..._fastjson parseobject 过滤值为null的字段

随便推点

Kubernetes + CRI + Kata + Firecracker -程序员宅基地

文章浏览阅读835次。KataKata源自希腊文Καταπίστευμα(ka-ta-PI-stev-ma),原意是值得信任的人,kata container正是解容器安全的问题而诞生的。传统的容器是基于namespace和cgroup进行隔离,在带来轻量简洁的同时,也带来了安全的隐患。事实上容器虽然提供一个与系统中的其它进程资源相隔离的执行环境,但是与宿主机系统是共享内核..._kata+stratovirt

[转]使用C#进行点对点通讯和文件传输(发送接收部分)-程序员宅基地

文章浏览阅读131次。上面介绍了通讯的基类,下面就是使用那个类进行发送和接收的部分: 二、发送部分: 发送咱们使用了多线程,可以同时进行多个任务,比如发送文件、发送文本等,互不影响: 发送文本方法: private void StartSendText(string strHost,int iPort,string strInfo) { SendText stText = new SendText(strHost,iP..._c# edi 报文发送接收

swift5.5异常的处理的三种的方式_swift5 运行时异常-程序员宅基地

文章浏览阅读179次。swift5.5异常的处理的三种的方式override func viewDidLoad() { super.viewDidLoad() method1() method2() method3() } func method3(){ //方式3 太危险,直接强制解包 不推荐 let pattern = "abc" let regex = try! NSRegularExpre_swift5 运行时异常

illumina 二代测序原理及过程_illumina测序原理及流程-程序员宅基地

文章浏览阅读1.1w次,点赞14次,收藏87次。illumina 二代测序原理及过程_illumina测序原理及流程

rosbag record -a -O cmd_record命令产生.bag.active,如何将其转换成.bag文件,执行rosbag play cmd_record.bag命令呢?_rosbag record生成bag-程序员宅基地

文章浏览阅读2.1k次,点赞2次,收藏12次。看了某站古月居的ROS视频,听着视频,看老师操作感觉很简单,自己跟着做了一遍,发现并不简单,为啥出现了好多奇怪的问题。 下面是解决将.bag.active转换成.bag文件的详细步骤!其中cmd_record是文件名。1、先用 $ rosrun turtlesim turtlesim_node 命令调出小海龟,然后让它走两步。2、之后用命令 $ rosbag record -a -O cmd_record 记录数据。..._rosbag record生成bag

Vue项目在Docker的自动化部署_vue+ts项目docker自动化部署-程序员宅基地

文章浏览阅读2.6k次。操作系统:CentOS部署环境:DockerCI/CD工具:Jenkins1.环境配置安装Jenkins:Centos安装Jenkins安装Docker:Centos安装Git、DotNet、Docker2.Github创建仓库https://github.com/longtaosu/Vue2DockerDemo3.本地初始化Vue项目初始化过..._vue+ts项目docker自动化部署

推荐文章

热门文章

相关标签