一篇文章让你精通:java集合讲解(五,哈希表)
Posted 韶光不负
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一篇文章让你精通:java集合讲解(五,哈希表)相关的知识,希望对你有一定的参考价值。
相信大家看过前面的内容后,对集合set有一定的了解,当我们重写定义对象时,要对对象的hashCode和equals方法进行重写。关于为什么我相信大家肯定和我有一样想法,所以小编此篇文章就来讲讲什么是哈希表。
哈希表原理
当在无序数组中按照内容进行查找,效率低下,时间复杂度O(N),在有序的数组中数组中按照内容查找,可以使用折半查找,时间复杂度O(long2N),在二叉平衡树中按照内容进行查找,时间复杂度O(long2N),按照数组索引进行查找,不进行比较与计数,直接计算得到时间复杂度O(1)(索引的位置=起始位置+元素大小*索引)
问题:不按索引查找,按内容进行查找不进行比较与计数直接计算得到时间复杂度O(1)?
有,这个就是哈希表。通过跟给定的字值进行比较,来进行比较,效率取决于比较次数。(理想状态下就是比较一次时间复杂度O(1))需要记录存储位置与其关键字之间确定的对应关系,使每一个记录位置与关键字相对应。
哈希表的特点
hashtable,哈希表也叫散列表,特点:快,非常快
结构:存在这很多种结构,最流行与好理解的是:顺序表+链表
主结构,顺序表,每一个顺序表节点单独引出链表
哈希表是如何添加数据的过程(非常重要)
1,计算哈希码,不管内容加入什么,都通过计算得到一个整数
@Override
public int hashCode() {
return Objects.hash(属性);
}
2,根据哈希码计算出在哈希表中存储位置(例如通过y=key(x)=x%11,根据取模安排在顺序表后面,如上图,x表示哈希码,key()表示函数“对应关系”,y表示存储位置)
3,存储进哈希表
一次添加成功 | 哈希表规则上没有,第一次添加成功 |
多次添加成功 | 出现冲突,调用equals方法与对应链表进行比较,比较到最后都是false时,创建新节点并存储数据,添加到链表末尾 |
不添加 | 出现冲突,调用equals方法与对应链表进行比较,比较到最后都是ture时,表明重复了,不添加 |
结论1:哈希表添加速度快(不考虑冲突情况下。三步就完成了),
结论2: 数据唯一并且无序
小问题:为什么重写hashCode就一定要重写equals方法?
原因:当我们重写hashCode方法后,当发生存储数据进入哈希表发生冲突时,我们就需要equals方法进行“比较”,来决定数据是否重复,选择添加或者不添加。
哈希表查询数据
与哈希表添加数据相同,
1,计算哈希码,
2,根据哈希码计算出在哈希表中存储位置
3,查找哈希表
一次查找 | 刚刚好查找的值在哈希表链表前面第一个位置 |
多次查找 | 查找的值不在哈希表链表前面第一个位置,不是目标,equals方法向下查找直到找到 |
找不到 | 查找的值不在哈希表链表前面第一个位置,不是目标,equals方法向下查找也没有找到目标 |
哈希表面试题
hashCode方法和equals方法有什么作用?
hashCode:计算哈希码,是一个整数,通过哈希码可以计算出数据哈希表中的位置。
equals:当添加出现冲突是,通过equals进行比较,看数据是否存在,同样在查找中使用判断数据是否正确
各种类型如何获取哈希码?
int:取自身(23-->23) (看 Interger 源码 )
double: 不可以取整 (看 double 源码,通过内部方法计算获取整 )
String: 通过字符串位置与编码值进行计算(例abc:(31*(31*(31*0*97)*98)*99) :a:编码值97,
b:编码值98,c:编码值99)
对象:对每一个对象的成员变量的哈希码,通过相加相乘进行计算(与字符串差不多)
为什么哈希码计算复杂?
哈希码相同则存储的内容相同,当哈希码复杂,哈希码相同概率减小就增加了哈希表的添加与查找速度。
如何减少冲突?
冲突是避免不了的,我们只能够减小冲突。
装填因子:哈希表的长度与表中记录数的长度的比例(表中记录数/哈希表长度)
当装填因子达到0.5(一般情况下取0.5)的时候,可以对主数组进行扩容。
方法:链表地址法,开放地址法,再散列法,建立公共溢出区。
以上是关于一篇文章让你精通:java集合讲解(五,哈希表)的主要内容,如果未能解决你的问题,请参考以下文章