java集合包HashMap

2023年 8月 22日 94.4k 0

HashMap实现原理

HashMap是Java中用于存储键值对的高性能数据结构。它基于哈希表(hash table)实现,用于快速查找、插入和删除操作。下面是HashMap的简要实现原理:

  • 哈希函数: HashMap使用哈希函数将键映射到数组的特定位置,这个位置称为桶(bucket)。哈希函数的目标是将键的范围映射到数组的索引范围内。Java中的Object类提供了hashCode()方法,它返回一个用于标识对象的整数哈希码。

  • 桶和链表/树: 数组的每个桶可能存储一个或多个键值对。当多个键映射到相同的桶时,就会发生哈希冲突。为了解决冲突,HashMap会在每个桶中使用链表(或红黑树)来存储相同桶位置上的键值对。当链表长度达到一定阈值时,链表可能会转换为树,以提高性能。

  • 存储和获取: 存储时,HashMap会计算键的哈希码,然后通过哈希函数找到对应的桶位置,如果桶为空,则直接将键值对存储在该桶中。如果桶中已经存在键值对,HashMap会遍历链表或树来查找是否有相同的键。获取时,HashMap也会计算键的哈希码,找到对应的桶位置,然后遍历链表或树来找到具体的值。

  • 扩容和重新哈希: 当HashMap中的键值对数量增多,桶中链表的长度可能会变长,导致查找效率降低。为了维护性能,HashMap会在适当的时候进行扩容操作。扩容时,HashMap会创建一个更大的数组,然后将现有的键值对重新分配到新的桶中,这个过程称为重新哈希。

  • 加载因子: 加载因子是一个比例,表示已存储键值对数量与桶总数之间的关系。当加载因子超过一定阈值时,就触发扩容。较高的加载因子可以减少空间浪费,但可能导致冲突增多。较低的加载因子可以减少冲突,但可能会浪费空间。在默认情况下,Java的HashMap加载因子为0.75。

  • 综上所述,HashMap通过哈希函数和桶来实现高效的键值对存储和查找,通过链表和树来解决冲突,通过扩容和重新哈希来维护性能。这使得HashMap在很多应用场景下都能够快速、高效地进行数据操作。

    示意图

    1、示意图1

    image.png

    2、示意图2

    image.png

    HashMap数组里的数据存储的是什么

    在Java中,HashMap是一种用于存储键-值对数据的数据结构。在HashMap数组中,存储的是键值对的信息。每个键值对由一个键(key)和一个对应的值(value)组成。HashMap使用哈希函数将键映射到数组中的特定位置,这样可以快速地通过键查找对应的值。

    简而言之,HashMap数组中存储的是键值对的信息,其中键用于唯一标识一个值,而值则是与键相关联的数据。这种数据结构可以高效地实现通过键查找值的操作。

    说白了,存储的就是节点(Node)。

    节点源码:java.util.HashMap.Node

    
        /**
         * Basic hash bin node, used for most entries.  (See below for
         * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
         */
        static class Node implements Map.Entry {
       
            final K key;
            V value;
            
            Node next; //指向下一个节点
    

    如果下标冲突,那么就是多个节点组成一个链表。

    数组的某个下标:只包含一个数据和包含多个数据的区别

    在HashMap的数组中,每个下标位置存储的数据可以包含一个或多个键值对。让我解释一下单个数据和多个数据的区别:

  • 单个数据:
    如果数组的某个下标位置只包含一个数据,这通常意味着在哈希表中没有冲突发生。也就是说,经过哈希函数计算后,该数据的键经过哈希映射得到的下标位置没有与其他键产生冲突,所以这个位置只存储了一个键值对。

  • 多个数据:
    当多个键通过哈希函数映射到了相同的下标位置时,就会发生哈希冲突。这意味着同一个数组下标位置需要存储多个键值对。为了解决冲突,HashMap使用一种链表或者更优化的数据结构(如红黑树)来存储相同下标位置的多个键值对。这样,即使多个键映射到同一个下标,也能够通过链表或树结构进行高效的查找和插入操作。

  • 总之,在HashMap的数组中,某个下标位置只包含一个数据表示没有冲突,而包含两个或更多数据则表示发生了哈希冲突,需要使用链表或树来存储多个键值对。

    示意图

    假设我们有一个包含5个位置的HashMap数组,每个位置可以存储一个键值对。现在我们来看两种情况:一个位置只包含一个数据和一个位置包含两个数据的情况。

  • 一个位置只包含一个数据:
  • Index 0: [Key1: Value1]
    Index 1: 
    Index 2: 
    Index 3: 
    Index 4: 
    

    在这种情况下,只有一个键值对被存储在数组的第一个位置(下标0)。

  • 一个位置包含两个数据(发生哈希冲突):
  • Index 0: [Key1: Value1] -> [Key6: Value6]
    Index 1: 
    Index 2: [Key2: Value2]
    Index 3: 
    Index 4: 
    

    在这种情况下,有两个键值对被存储在数组的第一个位置(下标0),因为Key1和Key6经过哈希函数后映射到了同一个位置。此时,使用链表或树结构来存储这两个键值对,以便可以高效地查找和插入。

    请注意,这只是一个简化的示意图,实际情况可能更加复杂,因为HashMap会自动进行扩容等优化操作。如果你需要更详细的图示,你可以在网上搜索关于HashMap的示意图和哈希冲突解决方法的图解。

    如果key冲突,新节点是查到前面还是插到后面?

    在HashMap中,当发生键(key)冲突时,新节点通常会被插入到已存在节点的后面,也就是通过链表或其他适当的数据结构连接到冲突位置的后面。

    当多个键经过哈希函数计算后映射到同一个数组下标位置时,HashMap会在该位置维护一个链表(或者在某些情况下,使用红黑树等更高效的数据结构)。每个链表节点都存储一个键值对。如果新节点与已存在节点发生冲突,新节点将被插入到链表的末尾,而不是前面。这种做法确保了插入操作的稳定性,并保持了之前的键值对顺序。

    然而,需要注意的是,随着不断插入新的节点,链表可能会变得很长,导致查找效率降低。为了应对这种情况,Java的HashMap在一些条件下会进行重新哈希(rehashing)和转换链表为红黑树等优化操作,以提高性能和保持合理的查找速度。

    示意图

    假设我们有一个HashMap数组,数组长度为5,下标从0到4。在下标0位置发生了哈希冲突,已经存在一个键值对 [Key1: Value1],现在要插入一个新的键值对 [Key6: Value6]。

    初始状态:

    Index 0: [Key1: Value1]
    Index 1: 
    Index 2: 
    Index 3: 
    Index 4: 
    

    在发生冲突后,数组下标0位置的链表如下所示(箭头表示链表连接):

    Index 0: [Key1: Value1] -> [Key3: Value3]
    Index 1: 
    Index 2: 
    Index 3: 
    Index 4: 
    

    现在要插入新的键值对 [Key6: Value6],它将被插入到链表的末尾,如下所示:

    Index 0: [Key1: Value1] -> [Key3: Value3] -> [Key6: Value6]
    Index 1: 
    Index 2: 
    Index 3: 
    Index 4: 
    

    请注意,这是一个简化的示意图,实际情况中,链表可能会根据实现和数据情况进行优化。另外,随着链表长度的增加,一些实现可能会触发优化操作,如链表转换为树,以保持性能。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论