皮皮网
皮皮网

【雷霆主图源码】【qtcreator设置源码】【-7的源码】hashmap源码原版

来源:英雄联盟s6架设源码 发表时间:2024-12-22 15:20:57

1.HashMap实现原理
2.hashmap底层实现原理
3.HashMap实现原理一步一步分析(1-put方法源码整体过程)
4.concurrenthashmap1.8源码如何详细解析?码原

hashmap源码原版

HashMap实现原理

        HashMap在实际开发中用到的频率非常高,面试中也是热点。所以决定写一篇文章进行分析,希望对想看源码的人起到一些帮助,看之前需要对链表比较熟悉。

        以下都是我自己的理解,欢迎讨论,写的不好轻喷。

        HashMap中的数据结构为散列表,又名哈希表。在这里我会对散列表进行一个简单的介绍,在此之前我们需要先回顾一下 数组、链表的优缺点。

        数组和链表的优缺点取决于他们各自在内存中存储的模式,也就是直接使用顺序存储或链式存储导致的。无论是数组还是链表,都有明显的缺点。而在实际业务中,我们想要的往往是寻址、删除、插入性能都很好的数据结构,散列表就是这样一种结构,它巧妙的结合了数组与链表的优点,并将其缺点弱化(并不是完全消除)

        散列表的做法是将key映射到数组的某个下标,存取的时候通过key获取到下标(index)然后通过下标直接存取。速度极快,而将key映射到下标需要使用散列函数,又名哈希函数。说到哈希函数可能有人已经想到了,如何将key映射到数组的下标。

        图中计算下标使用到了以下两个函数:

        值得注意的是,下标并不是通过hash函数直接得到的,计算下标还要对hash值做index()处理。

        Ps:在散列表中,数组的格子叫做桶,下标叫做桶号,桶可以包含一个key-value对,为了方便理解,后文不会使用这两个名词。

        以下是哈希碰撞相关的说明:

        以下是下标冲突相关的说明:

        很多人认为哈希值的碰撞和下标冲突是同一个东西,其实不是的,它们的正确关系是这样的,hashCode发生碰撞,则下标一定冲突;而下标冲突,hashCode并不一定碰撞

        上文提到,在jdk1.8以前HashMap的实现是散列表 = 数组 + 链表,但是到目前为止我们还没有看到链表起到的作用。事实上,HashMap引入链表的用意就是解决下标冲突。

        下图是引入链表后的散列表:

        如上图所示,左边的竖条,是一个大小为的数组,其中存储的是链表的头结点,我们知道,拥有链表的头结点即可访问整个链表,所以认为这个数组中的每个下标都存储着一个链表。其具体做法是,如果发现下标冲突,则后插入的节点以链表的形式追加到前一个节点的后面。

        这种使用链表解决冲突的方法叫做:拉链法(又叫链地址法)。HashMap使用的就是拉链法,拉链法是冲突发生以后的解决方案。

        Q:有了拉链法,就不用担心发生冲突吗?

        A:并不是!由于冲突的节点会不停的在链表上追加,大量的冲突会导致单个链表过长,使查询性能降低。所以一个好的散列表的实现应该从源头上减少冲突发生的可能性,冲突发生的概率和哈希函数返回值的均匀程度有直接关系,得到的哈希值越均匀,冲突发生的可能性越小。为了使哈希值更均匀,HashMap内部单独实现了hash()方法。

        以上是散列表的存储结构,但是在被运用到HashMap中时还有其他需要注意的地方,这里会详细说明。

        现在我们清楚了散列表的存储结构,细心的人应该已经发现了一个问题:Java中数组的长度是固定的,无论哈希函数是否均匀,随着插入到散列表中数据的增多,在数组长度不变的情况下,链表的长度会不断增加。这会导致链表查询性能不佳的缺点出现在散列表上,从而使散列表失去原本的意义。为了解决这个问题,HashMap引入了扩容与负载因子。

        以下是和扩容相关的一些概念和解释:

        Ps:扩容要重新计算下标,扩容要重新计算下标,扩容要重新计算下标,因为下标的计算和数组长度有关,长度改变,下标也应当重新计算。

        在1.8及其以上的jdk版本中,HashMap又引入了红黑树。

        红黑树的引入被用于替换链表,上文说到,如果冲突过多,会导致链表过长,降低查询性能,均匀的hash函数能有效的缓解冲突过多,但是并不能完全避免。所以HashMap加入了另一种解决方案,在往链表后追加节点时,如果发现链表长度达到8,就会将链表转为红黑树,以此提升查询的性能。

hashmap底层实现原理

       hashmap底层实现原理是SortedMap接口能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。

       å¦‚果使用排序的映射,建议使用TreeMap。在使用TreeMap时,key必须实现Comparable接口或者在构造TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常。

       Hashtable是遗留类,很多映射的常用功能与HashMap类似,不同的是它承自Dictionary类,并且是线程安全的,任一时间只有一个线程能写Hashtable

       ä»Žç»“构实现来讲,HashMap是:数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的。

扩展资料

       ä»Žæºç å¯çŸ¥ï¼ŒHashMap类中有一个非常重要的字段,就是 Node[] table,即哈希桶数组。Node是HashMap的一个内部类,实现了Map.Entry接口,本质是就是一个映射(键值对),除了K,V,还包含hash和next。

       HashMap就是使用哈希表来存储的。哈希表为解决冲突,采用链地址法来解决问题,链地址法,简单来说,就是数组加链表的结合。在每个数组元素上都一个链表结构,当数据被Hash后,得到数组下标,把数据放在对应下标元素的链表上。

       å¦‚果哈希桶数组很大,即使较差的Hash算法也会比较分散,如果哈希桶数组数组很小,即使好的Hash算法也会出现较多碰撞,所以就需要在空间成本和时间成本之间权衡,其实就是在根据实际情况确定哈希桶数组的大小,并在此基础上设计好的hash算法减少Hash碰撞。

HashMap实现原理一步一步分析(1-put方法源码整体过程)

       本文分享了HashMap内部的实现原理,重点解析了哈希(hash)、码原散列表(hash table)、码原哈希码(hashcode)以及hashCode()方法等基本概念。码原

       哈希(hash)是码原将任意长度的输入通过散列算法转换为固定长度输出的过程,建立一一对应关系。码原雷霆主图源码常见算法包括MD5加密和ASCII码表。码原

       散列表(hash table)是码原一种数据结构,通过关键码值映射到表中特定位置进行快速访问。码原

       哈希码(hashcode)是码原散列表中对象的存储位置标识,用于查找效率。码原

       Object类中的码原hashCode()方法用于获取对象的哈希码值,以在散列存储结构中确定对象存储地址。码原

       在存储字母时,码原使用哈希码值对数组大小取模以适应存储范围,码原防止哈希碰撞。

       HashMap在JDK1.7中使用数组+链表结构,qtcreator设置源码而JDK1.8引入了红黑树以优化性能。

       HashMap内部数据结构包含数组和Entry对象,数组用于存储Entry对象,Entry对象用于存储键值对。

       在put方法中,首先判断数组是否为空并初始化,然后计算键的哈希码值对数组长度取模,用于定位存储位置。-7的源码如果发生哈希碰撞,使用链表解决。

       本文详细介绍了HashMap的存储机制,包括数组+链表的实现方式,以及如何处理哈希碰撞。后续文章将继续深入探讨HashMap的其他特性,如数组长度的优化、多线程环境下的sql美化 源码性能优化和红黑树的引入。

concurrenthashmap1.8源码如何详细解析?

       ConcurrentHashMap在JDK1.8的线程安全机制基于CAS+synchronized实现,而非早期版本的分段锁。

       在JDK1.7版本中,ConcurrentHashMap采用分段锁机制,包含一个Segment数组,每个Segment继承自ReentrantLock,并包含HashEntry数组,每个HashEntry相当于链表节点,typora 查看源码用于存储key、value。默认支持个线程并发,每个Segment独立,互不影响。

       对于put流程,与普通HashMap相似,首先定位至特定的Segment,然后使用ReentrantLock进行操作,后续过程与HashMap基本相同。

       get流程简单,通过hash值定位至segment,再遍历链表找到对应元素。需要注意的是,value是volatile的,因此get操作无需加锁。

       在JDK1.8版本中,线程安全的关键在于优化了put流程。首先计算hash值,遍历node数组。若位置为空,则通过CAS+自旋方式初始化。

       若数组位置为空,尝试使用CAS自旋写入数据;若hash值为MOVED,表示需执行扩容操作;若满足上述条件均不成立,则使用synchronized块写入数据,同时判断链表或转换为红黑树进行插入。链表操作与HashMap相同,链表长度超过8时转换为红黑树。

       get查询流程与HashMap基本一致,通过key计算位置,若table对应位置的key相同则返回结果;如为红黑树结构,则按照红黑树规则获取;否则遍历链表获取数据。

相关栏目:焦点