HashMap源码解析

前言

在编程的世界里,有一种神奇的工具,它小巧却强大,灵活而可靠,它是Java中最常用的数据结构之一,它就是HashMap。然而,你是否曾经好奇过,这么强大的工具,其背后的实现原理是怎样的呢?今天,让我们一起揭开HashMap源码的神秘面纱,探索这个Java世界中的瑞士军刀。

首先,我们需要明白,HashMap并不是一个简单的哈希表,它是一个基于哈希表实现的Map接口的子类。哈希表是一种数据结构,它通过哈希函数将键值对映射到数组的一个位置,从而实现快速查找。而HashMap则在此基础上,增加了一些额外的功能和优化,使得它在处理大量数据时更加高效。

源码分析

那么,HashMap是如何工作的呢?它的工作原理可以简化为以下几个步骤:

  1. 初始化:当我们创建一个HashMap对象时,它会调用构造函数,创建一个新的数组和一个空的Node对象列表。

    /**
     * 系列构造方法,推荐在初始化时根据实际情况设置好初始容量,用好了可以显著减少 resize,提升效率
     */
    public HashMap(int initialCapacity, float loadFactor) {
         
         
        //检查初始容量是否小于0,如果是则抛出异常。
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                    initialCapacity);
        //检查初始容量是否大于最大容量,如果是则将初始容量设置为最大容量
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        //检查负载因子是否小于等于0或者是非数字,如果是则抛出异常。
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                    loadFactor);
        //将传入的负载因子赋值给当前对象的负载因子。
        this.loadFactor = loadFactor;
        //根据初始容量计算阈值并赋值给当前对象的阈值
        this.threshold = tableSizeFor(initialCapacity);
    }
    
    //调用带默认负载因子的构造方法,传入初始容量。
    public HashMap(int initialCapacity) {
         
         
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    //调用无参构造方法,将默认负载因子赋值给当前对象的负载因子。
    public HashMap() {
         
         
        this.loadFactor = DEFAULT_LOAD_FACTOR; 
    }
    //调用带映射参数的构造方法,将传入的映射赋值给当前对象的键值对,并将默认负载因子赋值给当前对象的负载因子。
    public HashMap(Map<? extends K, ? extends V> m) {
         
         
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }
    
    1. public HashMap(int initialCapacity, float loadFactor):这是一个带有初始容量和负载因子的构造方法。初始容量是HashMap在创建时可以容纳的元素数量,而负载因子是一个浮点数,表示HashMap在扩容之前可以达到的最大填充程度。如果初始容量小于0或者大于最大容量(MAXIMUM_CAPACITY),则会抛出异常。如果负载因子小于等于0或者是非数字(NaN),也会抛出异常。

    2. public HashMap(int initialCapacity):这是一个只带有初始容量参数的构造方法,它调用了上一个构造方法,并使用默认的负载因子(DEFAULT_LOAD_FACTOR)。

    3. public HashMap():这是一个无参构造方法,它设置了默认的负载因子(DEFAULT_LOAD_FACTOR)和其他字段的默认值。

    4. public HashMap(Map<? extends K, ? extends V> m):这是一个带有Map参数的构造方法,它首先设置了默认的负载因子,然后调用了putMapEntries方法将传入的Map中的所有键值对放入HashMap中。

  2. 插入:当我们向HashMap中插入一个键值对时,首先会使用键的hashCode()方法计算出其在数组中的一个位置,然后检查该位置是否已经有Node对象存在。如果不存在,则创建一个新的Node对象并放入该位置;如果存在,则更新该Node对象的value字段。

    public V put(K key, V value) {
         
         
        return putVal(hash(key), key, value, false, true);
    }
    
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict) {
         
         
            Node<K,V>[] tab; Node<K,V> p; int n, i;
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length; // 如果哈希表为空或者长度为0,则调用resize()方法进行扩容
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null); // 如果对应位置为空,则创建一个新的节点并放入该位置
            else {
         
         
                Node<K,V> e; K k;
                if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                    e = p; // 如果当前节点的哈希值和键都匹配,则将当前节点赋值给e
                else if (p instanceof TreeNode)
                    e = ((TreeNode<K,V>)p).putTre; // 如果当前节点是TreeNode类型,则调用putTre方法获取要插入的节点
                else {
         
         
                    for (int binCount = 0; ; ++binCount) {
         
         
                        if ((e = p.next) == null) {
         
         
                            p.next = newNode(hash, key, value, null); // 如果当前节点的下一个节点为空,则创建一个新的节点并放入当前节点的下一个位置
                            if (binCount >= TREEIFY_T
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全粘架构师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值