散列表又叫做哈希表(Hash Table),是一种提供了键(Key)值(Value)映射关系的一种数据结构。
只要给出一个Key值,就可以快速匹配出Value值。正是因为这个特性,比如有一本词典,它就可以给出一个单词,去快速找出它的解释。
什么是哈希函数?
首先,因为前面说过,数组的查询效率是最快的,数组可以根据数组的下标,直接对元素进行随机访问,所以,散列表想要查询快的话,其本质也是一个数组。
但是,对数组进行查询,需要的是数组下标,可这个数组下标怎么看都不像是传入的Key,因为key不仅可以用数字,也可用字符串,所以我们需要一个函数来将它们做转换,这个函数就叫做哈希函数。
像这张图,传入一个key3去为了找到value3,直接找是找不到的,中间经过哈希函数的转化,将key就变成了value3的数组下标,然后将value3返回。
散列表:写操作
哈希冲突
首先将写入的这组数据的Key通过哈希函数转化成数组的下标,比如说3。
现在数组下标为3的位置没有元素,就将该元素放入这个数组。
如果再来一个key,通过哈希函数也转化成了下标为3的数组位置,这就叫做哈希冲突。
解决哈希冲突的两个方法
1. 开放寻址法
数组中已经有了其他元素后,那就向后移动一位,看看下标为4的地方有没有元素,如果没有元素,就插入;如果有元素就继续向后寻址一位,直到没有元素的位置,然后插入。
这里很显然下一个位置没有元素,所以直接插入。
2. 链表法
这里的HashMap数组不仅仅是一个entry对象,还将作为一个链表的头节点。
如果刚才的情况,那只需要将entry对象的next指针指向它的下一个节点即可,如果再继续冲突,还将新元素继续插入对应链表。
散列表:读操作
比如说还是读这同个下标有两个元素的数组。
如果当key转化为数组的下标为3,先找到entry,发现entry的key无法与之相对应,则继续顺着next指针继续往链表的下一个元素寻找,找到entry,key与要查找的key相匹配,则停止,所以就返回entry2的value值。
散列表:扩容
扩容条件
HashMap.Size >= Capacity * LoadFactor
其中:
HashMap.Size:放入HashMap元素的个数;
Capacity:HashMap的当前长度;
LoadFactor:HashMap的负载因子,默认0.75f。
扩容方式
首先要创建一个新的空数组,其长度是原来的两倍。
最后又把原来的entry重新哈希到了新的数组中,因为数组的长度被扩大了,所以哈希的规则也随之改变了,元素的排列也比以前更松散了。