哈希表—HashMap
Map
集合的子类,HashMap
。
Java
中对于HashMap
的数据结构,是采用对象数组+链表
,在链表长度达到一定长度会转换成红黑二叉树;
讲哈希表之前,我们先来看一下API中的Object.hashCode
:
既然这个方法是属于Object
类的,那就说明所有的类也都有hashCode()
这个方法,所有对象都可以调用这个方法,返回对象的哈希码值。
支持此方法的优点是散列表,例如HashMap
中的散列表。
这里的支持此方法,指的是你的编写的子类重写了hashCode()
方法,优点是对散列表的性能进行的优化,散列表就是我们的哈希表。
首先public int hashCode()
返回的是一个int
值,它的作用是什么,为什么说hashCode()
可以提高哈希表的性能,原因很简单,哈希表的结构是这样的:
这种存储的效果有多好呢?如果我们要找一个数据,我们不需要对整个数组进行遍历,我们只需要得到它的哈希值进行取余操作,就可以得到它所在的下标,根本不需要遍历就可以很快的找到我们想要的数据。
假设我们还有一个对象的哈希值进行取余后也等于1,那我们应该把它放在哪里呢?我们的哈希表是数组+链表
,也就是说一个下标不仅仅只能存一个值,我们把这些值以链表的形式链起来,
O
b
j
e
c
t
→
O
b
j
e
c
t
Object→Object
Object→Object,俗称拉链法
:
当一个对象要存在1下标时,先看一下1下标里有没有数据,如果没有则直接存在链表的根节点,如果有一个数据则存在根节点的下一节点,以此类推;
在jdk1.8
版本时对哈希表进行了优化,当我们的一个下标中存储的数据达到了
8
8
8 个,再存储就会达到
9
9
9 时,也就是当一个数组的某一个下标的链表的长度超过
8
8
8 时,链表会转换成红黑树的形式,链表
→
→
→ 红黑树,它会更利于查找;
当然数组里面的每一个下标也有一个名词,它被称为哈希桶
;
所以完整的阐述:
当哈希桶中的数据量超过8时,从链表转换为红黑树(且数组长度 > 64,如果数组长度不大于64则对哈希桶数量扩容)
当哈希桶中的数据量减少到6时,从红黑树转换回链表
有的面试官很喜欢给你挖坑,他会问你:此时一个哈希桶中已知的数据量为7,那么当它减少到6时,一定会从红黑树转回链表吗?
大部分同学不思考直接下意识的就说出来,会!那么你就掉进坑里了!
因为此时我们的数据量可能从未到达过8,也就是一直以链表的方式存储,红黑树根本没有出现过,所以在描述的时候一定要把这种特殊情况描述出来。
以上就是哈希表的基础结构。
还有一些知识点:
- 初始桶的数量 16
- 散列因子 0.75
首先哈希桶的数量是会变化的,如果我们初始化时不给哈希桶的数量,那么它默认的数组长度就是16;
散列因子指的是什么呢?当我们要存10000个数据时,每一个哈希桶都会存好几百个数据,那么每一个哈希桶的链表长度都很长,它的性能会变得很低,即使它会优化成红黑树;散列因子 0.75 的含义就是当我们所有的桶已经有 75% 的桶都存上数据时,会对桶的数量进行扩容,桶会变得更多,它的扩容大小就是原长度的 2 倍,默认是16,从16扩容成了32,当然取余操作也要变成%32。
经官方测试 散列因子的 0.75 就是最佳值,如果散列因子是0.5,那么你的哈希桶永远只能存一半,但是它的占用空间大,效率高;如果给到0.99,那么这个哈希桶空间浪费的特别少,但是找数据就会比较慢。
API文档中的叙述:
构造方法:
以上就是Java
实现的哈希表,还是比较复杂的,对于性能有各种方面的考虑;
但比较常用的哈希表就是创建一个数组 然后根据哈希值往某个下标存,这就是一个简易的哈希表。