HashMap底层源码解读

141 阅读3分钟

HashMap是Java中广泛使用的数据结构之一,它提供了一个快速的键值存储映射,可以在常数时间复杂度内进行插入、删除和查找操作。本文将详细解析HashMap的底层源码实现原理,包括数据结构、哈希冲突解决方法以及扩容机制等。

1. 数据结构

HashMap的底层数据结构是一个数组,每个元素都是一个链表节点。当键值对插入HashMap时,会根据键的哈希值计算出数组索引,并在对应链表中插入节点。下面是HashMap的数据结构定义:

public class HashMap<K, V> extends AbstractMap<K, V>
    implements Map<K, V>, Cloneable, Serializable {

    // 默认初始容量为16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 

    // 最大容量为2^30
    static final int MAXIMUM_CAPACITY = 1 << 30;

    // 默认负载因子为0.75
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    // 链表节点定义
    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) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        // 省略getter和setter方法
    }

    // 存储数据的数组
    transient Node<K, V>[] table;

    // 存储键值对数量
    transient int size;

    // 扩容阈值
    int threshold;

    // 负载因子
    final float loadFactor;

    // 省略其他方法和属性
}

HashMap中的table数组用于存储数据,size属性表示当前键值对的数量,threshold属性是扩容阈值,loadFactor属性是负载因子。链表节点的定义中包含了键、值和哈希值等属性,以及指向下一个节点的指针next

2. 哈希冲突解决方法

当两个键的哈希值相同时,会发生哈希冲突。HashMap使用了一个“拉链法”来解决哈希冲突,即将哈希值相同的键值对存储在同一个链表中。当新的键值对插入到已有链表中时,会按照插入顺序放在链表的末尾。

在Java 8及以后版本中,当链表中的元素数量达到一定阈值时,链表会转化为红黑树,以提高查找效率。当红黑树中的元素数量减少到一定程度时,会再次转化为链表。

3. 哈希函数

HashMap使用的哈希函数是Java中的标准哈希函数,即取模运算:h = key.hashCode() % capacity,其中key.hashCode()是键的哈希值,capacity是数组的长度。在实际应用中,取模运算可能会出现哈希碰撞,因此在计算哈希值时,HashMap会对哈希值进行一定的修正。

在Java 8及以后版本中,为了进一步减少哈希碰撞,HashMap引入了一种新的哈希函数:(h = key.hashCode()) ^ (h >>> 16),其中>>>是无符号右移运算。这个哈希函数将键的哈希值高16位与低16位进行异或运算,以增加哈希函数的随机性,减少碰撞概率。

4. 扩容机制

当HashMap中的元素数量达到扩容阈值时,会自动进行扩容。扩容过程涉及到数组的重新分配和数据的迁移,是一个比较耗时的操作。

在扩容时,HashMap会先将数组的长度翻倍,然后将每个链表中的元素重新计算哈希值,并放入新数组的对应位置中。由于新数组的长度是原数组长度的两倍,因此在新数组中,元素的分布会更加分散,减少哈希碰撞的概率。同时,在扩容过程中,HashMap会重新计算扩容阈值,并更新相关属性。

5. 总结

本文对HashMap的底层源码进行了详细分析,包括数据结构、哈希冲突解决方法、哈希函数以及扩容机制等。通过深入了解HashMap的底层实现原理,可以更好地理解和应用HashMap,避免出现性能问题和错误。