Java中HashMap底层实现原理(代码解释),从源码角度分析,HashMap起初为什么不使用别的方案替代?

      HashMap是Java集合框架中的一个重要类,它提供了一种键值对存储的方式,可以快速地进行查找、插入和删除操作。HashMap的底层实现是基于哈希表的,本文将从源码角度分析HashMap的底层原理,并通过案例展示其具体使用方法。

HashMap的底层实现

      HashMap的底层实现是基于哈希表的,它通过将键值对映射到一个数组中的某个位置来实现快速的查找、插入和删除操作。具体来说,HashMap将键值对存储在一个Entry数组中,每个Entry对象包含一个键、一个值和一个指向下一个Entry对象的指针,形成一个链表。当插入一个新的键值对时,HashMap会根据键的哈希值计算出该键值对在数组中的位置,然后将该键值对插入到相应的链表中。当查找一个键值对时,HashMap会先根据键的哈希值计算出该键值对在数组中的位置,然后遍历相应的链表,查找是否存在该键值对。 

      HashMap的底层实现还涉及到两个重要的概念:负载因子和扩容。负载因子是指哈希表中已存储键值对数量与数组长度之比,当负载因子超过一定阈值时,HashMap会触发扩容操作,即将数组长度增加一倍,并重新计算每个键值对在数组中的位置。这样做的目的是为了保证哈希表的性能。当负载因子过高时,哈希表的冲突率会增加,导致查找、插入和删除操作的性能下降。扩容操作可以减少哈希表的冲突率,提高操作性能。

HashMap的使用案例

      下面通过一个简单的案例来展示HashMap的使用方法。

import java.util.HashMap;

public class HashMapDemo {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("apple", 1);
        map.put("banana", 2);
        map.put("orange", 3);
        System.out.println(map.get("apple")); // 输出 1
        System.out.println(map.containsKey("banana")); // 输出 true
        System.out.println(map.containsValue(3)); // 输出 true
        map.remove("orange");
        System.out.println(map); // 输出 {apple=1, banana=2}
    }
}

      上面的代码创建了一个HashMap对象,存储了三个键值对。然后通过get、containsKey、containsValue和remove方法对键值对进行操作。最后输出HashMap对象的内容。

源码分析

      HashMap的源码非常复杂,涉及到多个类和接口。这里只对其中的一部分进行分析。

      HashMap类是HashMap的主要实现类,它实现了Map接口。HashMap内部维护了一个Entry数组,每个Entry对象包含一个键、一个值和一个指向下一个Entry对象的指针。HashMap还维护了一个负载因子和一个阈值,用于触发扩容操作。当HashMap的大小超过阈值时,会触发扩容操作。

      HashMap的put方法用于插入一个键值对。具体实现如下:

public V put(K key, V value) {
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
    modCount++;
    addEntry(hash, key, value, i);
    return null;
}

      该方法首先判断数组是否为空,为空则进行初始化。然后计算出键值对在数组中的位置,遍历相应的链表,查找是否存在该键值对。如果存在,则更新值并返回旧值;否则将键值对插入到链表的头部,并返回null。

HashMap的get方法用于查找一个键对应的值。具体实现如下:

public V get(Object key) {
    if (key == null)
        return getForNullKey();
    Entry<K,V> entry = getEntry(key);
    return null == entry ? null : entry.getValue();
}

      该方法首先判断键是否为null,如果是,则调用getForNullKey方法查找对应的值。否则调用getEntry方法查找对应的Entry对象,并返回其值。

      HashMap的getEntry方法用于查找一个键对应的Entry对象。具体实现如下:

final Entry<K,V> getEntry(Object key) {
    int hash = (key == null) ? 0 : hash(key);
    for (Entry<K,V> e = table[indexFor(hash, table.length)];
         e != null;
         e = e.next) {
        Object k;
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k))))
            return e;
    }
    return null;
}

      该方法首先计算出键的哈希值,然后根据哈希值计算出键值对在数组中的位置,遍历相应的链表,查找是否存在该键值对。如果存在,则返回对应的Entry对象;否则返回null。

为什么HashMap不使用别的方案替代?

HashMap的底层实现是基于哈希表的,它具有以下优点:

  • 快速查找:哈希表可以通过将键值对映射到一个数组中的某个位置,从而实现快速的查找操作。
  • 高效插入和删除:哈希表可以通过将键值对插入到相应的链表中,从而实现高效的插入和删除操作。
  • 自动扩容:哈希表可以根据负载因子和阈值自动触发扩容操作,从而保证哈希表的性能。

除了上述优点,HashMap的底层实现还具有以下特点:

  • 支持null键和null值:HashMap可以存储null键和null值,这是因为在计算哈希值时会将null键的哈希值设置为0,而null值可以直接存储在Entry对象中。
  • 非线程安全:HashMap的底层实现是非线程安全的,如果多个线程同时对HashMap进行操作,可能会导致数据不一致的问题。如果需要在多线程环境下使用HashMap,可以考虑使用ConcurrentHashMap或者对HashMap进行加锁。
  • 遍历顺序不确定:HashMap的遍历顺序并不是按照插入顺序或者键的大小顺序进行的,而是根据哈希值计算得到的位置顺序进行的。因此,遍历HashMap的顺序是不确定的。

      总之,HashMap的底层实现是一种非常优秀的方案,它提供了快速的查找、插入和删除操作,并且支持自动扩容和存储null键和null值。但是需要注意的是,HashMap是非线程安全的,遍历顺序也是不确定的,使用时需要根据具体情况进行选择。

以上内容均为个人总结,如有雷同,纯属巧合!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值