我是靠谱客的博主 凶狠烧鹅,最近开发中收集的这篇文章主要介绍Hash原理与HashMap,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(46)

评论列表共有 0 条评论

立即
投稿
返回
顶部