概述
1.背景
对于数据集合我们可以使用数组,链表等结构来存储。
数组在内存中是一段连续的存储空间,所以当进行删除或者插入操作时,需要对影响到的数据重新前移或者后移,性能较低;
而链表内存空间不连续,但是在执行查询操作时,需要逐一遍历结点,性能较低;
在大多数情况下,对于数据集合的操作包含了数据存储(put)与数据访问(get),所以需要一种新的结构来提高数据集合存储和访问的效率。
2.Hash的原理
在查找数据时,我们依据的往往是一个数据对象中的某一个属性,如查找张三的成绩,这就有了两个属性值,name与score,对应在Hash中就是Key-Value,通过key “张三”,查找其Value“成绩”。
与数组类似,Hash在构造时也是要先申请一块连续的空间。
存储时,将key通过函数hash(key)得到一个hashcode,然后将hashcode映射到Hash表对应的空间索引;这样在访问时,通过key可以直接找到value。
一个很简单的例子,
姓名 成绩
张三 80
李四 90
需求:查找张三,李四的成绩
首先可以确定以姓名作为key,然后函数hash()我们可以设计为姓氏的笔画数,即
hash("张") = 7
hash("李") = 6
所以在存储<"张三",80>,<"李四",90>时,通过hash(),分别将两组数据存储在Hash构造时申请的空间索引7,6的位置。
在访问时,查找张三的成绩,通过hash("张三")得到hashcode为7,然后可以直接访问,无需一一比对。
3.冲突处理
当数据量较大时,我们不可能通过设计一种hash()算法实现,通过key得到的hashcode是唯一的,所以会出现key不同,但是hashcode相同的情况,这就产生了冲突
解决冲突,一般有两种解决方法
1.开放地址法,冲突发生时,按照一定的序列去寻找开放的地址,也就是没有冲突的位置,以此作为存储位置。
2.拉链法,冲突发生时,在冲突的位置,构造一个链表用来存储数据,如下图所示
3.负载因子
Load factor,负载因子=存入hash表中的元素个数/hash表的长度,一般负载因子会有一个标准,当超过这个标准时,会重新分配空间
有这样一个规律,当负载因子较大时,也就是元素个数较多,发生冲突的可能性较大,性能就会降低
当负载因子较小小时,也就是元素的个数较少,发生冲突的可能性较小,性能会提高,但是会带来内存的增加。
下图是是一个负载因子与时间和空间的关系图:
参照:http://www.blogjava.net/dongbule/archive/2011/02/15/344387.html
3.HashMap
Java中存储数据集合的类结构如下所示:
Collection<--List<--Vector
Collection<--List<--ArrayList
Collection<--List<--LinkedList
Collection<--Set<--HashSet
Collection<--Set<--HashSet<--LinkedHashSet
Collection<--Set<--SortedSet<--TreeSet
Map<--SortedMap<--TreeMap
Map<--HashMap
详情参照:
http://skyuck.iteye.com/blog/526358
我们看一下HashMap的实现
在HashMap中将Key-Value封装成了Entry类,在Entry类中还包含了一个Entry的指针(引用),当冲突发生时用来指向新加入的元素。Entry是利用HashMap存储的基本单位。
内存申请:一般申请的内存长度为2的n次方,大于存储元素的个数
Hash值:利用h(key.hashCode())计算出要插入元素的哈希值,其中key.hashCode()是key的hash码,每个对象在生成时,系统根据对象的地址以及其他的参数生成的唯一的一个hashCode(). h(int h)很据
Index:对于生成的hash值,需要尽可能均匀的分布到申请的连续内存中,一般来说我们是利用h%length取模运算来分配,但是“摸”运算事实上消耗还是很大的,HashMap利用了h & (length-1)来获取index,我们会发现因为我们申请的length是2的n次方,然后减一之后,变成了“00000001111...”然后与h做与运算,结果和h%length是一样的,但是运算的消耗却少了很多。
存储:利用了拉链法,当冲突发生时,在当前位置上构造链表,将新加入的元素插入表头。
访问:利用key获取hashCode,然后用hash函数得到hash值,在获取index值,在index处,对比key,直至找到正确的value。
扩容:HashMap默认的负载因子是0.75,当超过这个值时,HashMap就会扩容,一般是扩展为现有长度的2倍,然后重新计算每个元素的位置。
参照:http://beyond99.blog.51cto.com/1469451/429789
http://www.tudou.com/programs/view/Dc_ccF1i8M0/
最后
以上就是凶狠烧鹅为你收集整理的Hash原理与HashMap的全部内容,希望文章能够帮你解决Hash原理与HashMap所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复