技术标签: 学习 java基础 java 源码 hashmap 数据结构
转自:http://www.jianshu.com/p/dff8f4641814
前几天在一个群里看到有人讨论hashmap中的加载因子为什么是默认0.75。
HashMap源码中的加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
当时想到的是应该是“哈希冲突”和“空间利用率”矛盾的一个折衷。
跟数据结构要么查询快要么插入快一个道理,hashmap就是一个插入慢、查询快的数据结构。
加载因子是表示Hsah表中元素的填满的程度。
加载因子越大,填满的元素越多,空间利用率越高,但冲突的机会加大了。
反之,加载因子越小,填满的元素越少,冲突的机会减小,但空间浪费多了。
冲突的机会越大,则查找的成本越高。反之,查找的成本越小。
因此,必须在 "冲突的机会"与"空间利用率"之间寻找一种平衡与折衷。
本着不嫌事大的精神继续深挖,在此之前先简单补充点本文需要的基础知识:
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=伪随机数序列。还是那个例子,这是完全根据心情去选一家店来问了
缺点:
Hi = RHi(key),i=1,2,...k
RHi均是不同的哈希函数,即在同义词产生地址冲突时计算另一个哈希函数地址,直到不发生冲突为止。这种方法不易产生聚集,但是增加了计算时间。
缺点:增加了计算时间。
假设哈希函数的值域为[0,m-1],则设向量HashTable[0...m-1]为基本表,每个分量存放一个记录,另设立向量OverTable[0....v]为溢出表。所有关键字和基本表中关键字为同义词的记录,不管他们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入溢出表。
简单地说就是搞个新表存冲突的元素。
将所有关键字为同义词的记录存储在同一线性链表中,也就是把冲突位置的元素构造成链表。
拉链法的优点:
拉链法的缺点:
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
欢迎讨论,这个周末的任务完成了,可以放心去浪了─=≡Σ((( つ•̀ω•́)つ超人
文章浏览阅读1k次。概念与操作描述一种用于在集成电路之间进行数据传输的串行通信协议。主设备(Master)发起通信并控制整个数据传输过程的设备。从设备(Slave)被动响应主设备请求的设备。数据线(SDA)双向的数据线,用于传输数据。时钟线(SCL)主设备通过时钟线控制数据的传输速率。地址每个从设备在总线上具有唯一的地址,用于识别设备。读写操作主设备向从设备发送读取或写入命令,用于读取从设备的数据或向从设备写入数据。应答(ACK/NACK)_esp32 mpu6050
文章浏览阅读736次。第5章 商品搜索学习目标Elasticsearch安装docker安装Elasticsearch系统参数问题跨域操作IK分词器配置Kibana的使用->DSL语句Kibana->DSL语句操作->ElasticsearchES导入商品搜索数据Sku数据导入到ElasticsearchMap数据类型->Object关键词搜索->能够实现搜索流程代码的编写分类统计搜索1. Elasticsearch 安装我们之前已经使_商城系统 es商品搜索排序
文章浏览阅读3.1k次,点赞4次,收藏16次。参考文章:百度AI攻略:出租车票识别_才能我浪费的博客-程序员宅基地附完整python源码)基于tensorflow、opencv的入门案例_发票识别一:关键区域定位_小白来搬家-程序员宅基地_python发票识别注:感谢一起完成项目的队友们大赛官网:出租车发票识别 Competitions - DataFountain一、赛题说明1.赛题背景出租车发票在日常财务发票报销中较为常见,由于这类发票样式丰富,区域性特点明显,并且包含大量模糊字迹和错位字迹,因此准确的定位发票_出租车发票识别ccf
文章浏览阅读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..._线性表完整程序代码
文章浏览阅读2.7k次,点赞2次,收藏2次。fastjson转换字符串自动过滤value为null的问题背景:使用 JSON.toJSONString(Object object)的时候发现打印出来的字符串少了几个字段,以为代码没走相应的逻辑,发现少的都是value为null的字段,发现是fastjson自动把value为null的字段过滤了。解决办法:设置SerializerFeature序列化属性 SerializerFeatu..._fastjson parseobject 过滤值为null的字段
文章浏览阅读835次。KataKata源自希腊文Καταπίστευμα(ka-ta-PI-stev-ma),原意是值得信任的人,kata container正是解容器安全的问题而诞生的。传统的容器是基于namespace和cgroup进行隔离,在带来轻量简洁的同时,也带来了安全的隐患。事实上容器虽然提供一个与系统中的其它进程资源相隔离的执行环境,但是与宿主机系统是共享内核..._kata+stratovirt
文章浏览阅读131次。上面介绍了通讯的基类,下面就是使用那个类进行发送和接收的部分: 二、发送部分: 发送咱们使用了多线程,可以同时进行多个任务,比如发送文件、发送文本等,互不影响: 发送文本方法: private void StartSendText(string strHost,int iPort,string strInfo) { SendText stText = new SendText(strHost,iP..._c# edi 报文发送接收
文章浏览阅读179次。swift5.5异常的处理的三种的方式override func viewDidLoad() { super.viewDidLoad() method1() method2() method3() } func method3(){ //方式3 太危险,直接强制解包 不推荐 let pattern = "abc" let regex = try! NSRegularExpre_swift5 运行时异常
文章浏览阅读1.1w次,点赞14次,收藏87次。illumina 二代测序原理及过程_illumina测序原理及流程
文章浏览阅读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
文章浏览阅读2.6k次。操作系统:CentOS部署环境:DockerCI/CD工具:Jenkins1.环境配置安装Jenkins:Centos安装Jenkins安装Docker:Centos安装Git、DotNet、Docker2.Github创建仓库https://github.com/longtaosu/Vue2DockerDemo3.本地初始化Vue项目初始化过..._vue+ts项目docker自动化部署