HashMap 的底层原理和源码分析

tip:作为程序员一定学习编程之道,一定要对代码的编写有追求,不能实现就完事了。我们应该让自己写的代码更加优雅,即使这会费时费力。

推荐:体系化学习Java(Java面试专题)

文章目录

  • 一、HashMap 的底层原理
  • 二、put方法源码分析
  • 三、get 方法源码分析
  • 四、remove 方法源码分析

一、HashMap 的底层原理

HashMap 是 Java 中常用的一种数据结构,它是基于哈希表实现的。下面是 HashMap 的底层原理:

HashMap 内部维护了一个数组,称为哈希表,每个元素都是一个链表的头结点,该链表被称为桶(bucket)。当我们向 HashMap 中添加一个元素时,首先会根据该元素的键值(key)计算出一个哈希值(hash code),然后根据哈希值确定该元素在数组中的位置,如果该位置上已经存在了元素,则将该元素添加到该位置上对应的桶中,如果该位置上没有元素,则创建一个新的桶,并将该元素添加到该桶中。

HashMap 的底层原理和源码分析_第1张图片

HashMap 的哈希值是通过调用键值的 hashCode 方法计算得到的。由于哈希值可能会发生冲突,因此每个桶中可能会有多个元素。当我们需要查找元素时,首先根据键值的哈希值确定该元素所在的桶,然后遍历该桶中的所有元素,找到与给定键值相等的元素并返回。

为了提高 HashMap 的查找效率,Java 8 中引入了红黑树(Red-Black Tree)的概念。当一个桶中的元素个数达到一定阈值(默认为 8)时,该桶中的元素将被转化为一棵红黑树,从而提高查找效率。当然,如果桶中的元素个数小于等于阈值,则仍然采用链表的方式进行存储。

HashMap 的扩容机制是在数组大小达到一定阈值(默认为 75%)时进行的。当数组大小达到阈值时,HashMap 会创建一个新的数组,将原数组中的元素重新哈希到新数组中,并将新数组作为 HashMap 的哈希表。这个过程需要重新计算每个元素在新数组中的位置,因此比较耗时。

HashMap 是非线程安全的,如果多个线程同时对 HashMap 进行修改,可能会导致数据不一致的情况。如果需要在多线程环境下使用 HashMap,可以使用 ConcurrentHashMap。

HashMap 的底层原理和源码分析_第2张图片

二、put方法源码分析

public V put(K key, V value) {
   
    // 计算 key 的哈希值
    int hash = hash(key);
    
    // 根据哈希值和数组长度计算出 key 在数组中的位置
    int i = indexFor(hash, table.length);
    
    // 遍历该位置上的链表,查找是否已经存在该 key
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        // 如果该 key 已经存在,则更新其对应的 value,并返回旧的 value
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
    
    // 如果该 key 不存在,则将其添加到链表的头部
    modCount++;
    addEntry(hash, key, value, i);
    return null;
}

put 方法的主要流程如下:

  1. 计算 key 的哈希值。
  2. 根据哈希值和数组长度计算出 key 在数组中的位置。
  3. 遍历该位置上的链表,查找是否已经存在该 key,如果存在,则更新其对应的 value,并返回旧的 value。
  4. 如果该 key 不存在,则将其添加到链表的头部。

在添加元素时,HashMap 会将元素添加到链表的头部,这是因为在遍历链表时,我们通常会从头部开始遍历,这样可以提高查找效率。如果链表中已经存在该 key,则将其对应的 value 更新为新的值。如果链表中不存在该 key,则将其添加到链表的头部,并将 modCount 属性加 1,表示 HashMap 的结构已经发生了变化。

三、get 方法源码分析

public V get(Object key) {
    
    // 如果 key 为 null,则返回 null
    if (key == null)
        return getForNullKey();
    
    // 计算 key 的哈希值
    int hash = hash(key);
    
    // 根据哈希值和数组长度计算出 key 在数组中的位置
    for (Entry<K,V> e = table[indexFor(hash, table.length)];
         e != null;
         e = e.next) {
        Object k;
        // 如果找到了对应的 key,则返回其对应的 value
        if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
            return e.value;
    }
    
    // 如果没有找到对应的 key,则返回 null
    return null;
}

get 方法的主要流程如下:

  1. 如果 key 为 null,则返回 null。
  2. 计算 key 的哈希值。
  3. 根据哈希值和数组长度计算出 key 在数组中的位置。
  4. 遍历该位置上的链表,查找是否存在对应的 key,如果存在,则返回其对应的 value。
  5. 如果链表中不存在对应的 key,则返回 null。

在查找元素时,HashMap 会根据 key 的哈希值确定其在数组中的位置,然后遍历该位置上的链表,查找是否存在对应的 key。如果存在,则返回其对应的 value;如果不存在,则返回 null。由于哈希值可能会发生冲突,因此同一个桶上可能会有多个元素,需要遍历链表才能找到对应的元素。

四、remove 方法源码分析

public V remove(Object key) {
    Entry<K,V> e = removeEntryForKey(key);
    return (e == null ? null : e.value);
}

final Entry<K,V> removeEntryForKey(Object key) {
   // 如果 key 为 null,则返回 null
   if (key == null)
       return null;
  
   // 计算 key 的哈希值
   int hash = hash(key);
   
   // 根据哈希值和数组长度计算出 key 在数组中的位置
   int i = indexFor(hash, table.length);
  
   // 遍历该位置上的链表,查找是否存在对应的 key
   Entry<K,V> prev = table[i];
   Entry<K,V> e = prev;
   while (e != null) {
       Entry<K,V> next = e.next;
       Object k;
       // 如果找到了对应的 key,则将其从链表中删除
       if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
           modCount++;
           size--;
           if (prev == e)
               table[i] = next;
           else
               prev.next = next;
           e.recordRemoval(this);
           return e;
       }
       prev = e;
       e = next;
   }
   
   // 如果没有找到对应的 key,则返回 null
   return null;
}

remove 方法的主要流程如下:

  1. 调用 removeEntryForKey 方法查找对应的 Entry 对象。
  2. 如果找到了对应的 Entry 对象,则将其从链表中删除,并返回其对应的 value。
  3. 如果没有找到对应的 Entry 对象,则返回 null。

在删除元素时,HashMap 会先调用 removeEntryForKey 方法查找对应的 Entry 对象,然后将其从链表中删除。如果找到了对应的 Entry 对象,则将其从链表中删除,并返回其对应的 value;如果没有找到对应的 Entry 对象,则返回 null。

你可能感兴趣的:(Java,基础,java,数据结构,哈希算法)