Map实现之HashMap
场景引入
有这么几个数据 : 斗破苍穹、斗罗大陆、一人之下、灌篮高手、海贼王。将这些数据存储到数组中。此时要查找灌篮高手的数据,正常是采用遍历的方式,情况好的时候,很快就能找到,情况糟糕的时候最后一个找到,时间复杂度是O(n)。如果这个数组很大,查找性能是很低的。
优化方案: 如果存的数据是数字类型的,如 10 , 3 ,8 ,9这这样的数据,是可以采用有序的方式存储,同样是存储到数组中,因为它是有序的,可以采用二分查找的方式,性能有一定的提升。
有没有更快的查找方式呢???
散列哈希表
采用散列表存储,散列表(Hash table,也叫哈希表),是根据键值对(key-value)的结构而直接进行访问的数据结构。通过 key值通过一个具体的函数计算得到一个数值(就是一个具体的数字,等价于索引下标),这个数值映射到哈表标的下标,找到下标就能访问对应上的元素。这个映射函数叫做散列函数,也叫哈希函数。存放记录的数组叫做散列表。
put 操作,要经过如下步骤:
1) key值经过哈希函数计算,得到一个哈希值 记作 hash
2) 对 hash 再计算得到一个数字 ,取值范围在哈希表的索引范围内,记作 index
3)通过index,访问哈希表的地址,把value放到这个地址上(固定的封装结构)
get 操作,要经过如下步骤:
1) key值经过哈希函数计算,得到一个哈希值 记作 hash
2) 对 hash 再计算得到一个数字 ,取值范围在哈希表的索引范围内,记作 index
3)通过index,访问哈希表的地址,把index位置上的数据取出来,得到value
哈希表读取的时间复杂度是O(1) .
思考hash表的几个问题:
1、hash值计算时候,哈希函数出现哈希冲突如何解决
- 开放地址法
- 拉链法(冲突的位置转换成链表)
2、hash函数如何设计才算合理
- 考虑哈希表的大小
- 考虑尽可能少的哈希冲突
- 考虑遇到哈希冲突,如何解决
3、hash散列,扩容和缩容
java中的HashMap
key-value 的结构封装成 Entity,Entity 有一个实现类Node。
Entity 接口接口如下:
interface Entry<K,V> {
K getKey();
V getValue();
V setValue(V value);
boolean equals(Object o);
int hashCode();
// 等等
Node 类结构如下:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
// 省略
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
// 省略实现
}
public final boolean equals(Object o) {
// 省略实现
}
}
HashMap 的数据结构
HashMap 底层数据结构是有一个 hash table(哈希表)组成,这个哈希表采用数组的方式实现。
transient Node<K,V>[] table;
// 其中 Node 是 Entry 接口的一个实现类。
哈希表上的每个位置叫做 bucket,哈希桶 (最简单的理解就是 table数组上的元素)。
hash因子
1、hash 因子为什么是2的整数次幂
hash函数
hash冲突
1.7版本实现
HashMap的底层数据结构
hash值计算以及hash冲突
扩容和缩容
1.8版本实现jdk在实现上的变化
链表转红黑树,当链表的长度达到阈值,链表转换为红黑树。