Map实现之HashMap原理分析

99 阅读3分钟

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 的数据结构

image.png

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在实现上的变化

链表转红黑树,当链表的长度达到阈值,链表转换为红黑树。

什么条件出触发链表转红黑树

什么条件出阿发红黑树转链表

链表转红黑树带来哪些改变