今天依旧是元气满满的小黄鸭,礼拜六?不存在的
1、什么是hash?
Hash:是一种信息摘要算法,它还叫做哈希,或者散列。我们平时使用的MD5,SHA1都属于Hash算法,通过输入key进行Hash计算,就可以获取key的HashCode(),比如我们通过校验MD5来验证文件的完整性。
对于HashCode,它是一个本地方法,实质就是地址取样运算
2、什么是碰撞?
好的Hash算法可以出计算几乎出独一无二的HashCode,如果出现了重复的hashCode,就称作碰撞;
就算是MD5这样优秀的算法也会发生碰撞,即两个不同的key也有可能生成相同的MD5。
3、“你知道HashMap的工作原理吗?” “你知道HashMap的get()方法的工作原理吗?”
- HashMap 是一个散列桶(数组和链表),它存储的内容是键值对 key-value 映射
- HashMap 采用了数组和链表的数据结构,能在查询和修改方便继承了数组的线性查找和链表的寻址修改
- HashMap 是非 synchronized,所以 HashMap 很快
- HashMap 可以接受 null 键和值,而 Hashtable 则不能(原因就是 equlas() 方法需要对象,因为 HashMap 是后出的 API 经过处理才可以)
HashMap 是基于 hashing 的原理
我们使用 put(key, value) 存储对象到 HashMap 中,使用 get(key) 从 HashMap 中获取对象。当我们给 put() 方法传递键和值时,我们先对键调用 hashCode() 方法,计算并返回的 hashCode 是用于找到 Map 数组的 bucket 位置来储存 Node 对象。
这里关键点在于指出,HashMap 是在 bucket 中储存键对象和值对象,作为Map.Node 。
以下是 HashMap 初始化
简化的模拟数据结构:
1 2 3 4 5 6 7 |
|
以下是具体的 put 过程(JDK1.8)
- 对 Key 求 Hash 值,然后再计算下标
- 如果没有碰撞,直接放入桶中(碰撞的意思是计算得到的 Hash 值相同,需要放到同一个 bucket 中)
- 如果碰撞了,以链表的方式链接到后面
- 如果链表长度超过阀值(TREEIFY THRESHOLD==8),就把链表转成红黑树,链表长度低于6,就把红黑树转回链表
- 如果节点已经存在就替换旧值
- 如果桶满了(容量16*加载因子0.75),就需要 resize(扩容2倍后重排)
以下是具体 get 过程
考虑特殊情况:如果两个键的 hashcode 相同,你如何获取值对象?
当我们调用 get() 方法,HashMap 会使用键对象的 hashcode 找到 bucket 位置,找到 bucket 位置之后,会调用 keys.equals() 方法去找到链表中正确的节点,最终找到要找的值对象。
4、如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?
默认的负载因子大小为0.75,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程为rehashing,因为它调用hash方法找到新的bucket位置