`

hashmap源码学习,如何查找索引,解决冲突

 
阅读更多

之前看过一部分jdk的源码,发现看了之后又忘了,或者是看的不够深入,有次去面试:问我hashmap怎么实现的,结果我答的不怎么样,没答出一些关键部分,现在再重新开读读jdk的util包下的一些源代码:

特别声明:小弟发博客纯属学习,若有错误,不当之处请指出!!!

 

首先,我们大家都知道hashmap内部用的是散列实现,但是具体怎么实现的呢?如果解决冲突的呢?

实现原理:

hashmap用数组实现,数组中存放的是Entry类型的元素,每个Entry元素其实是一个key-value对,持有下一个元素的引用,这就说明table数组中的Entry元素还是某个Entry链表的首节点,指向该链表的下一个元素,所以hashmap用“数组链表”实现的;

这个是网上找的hashmap底层数据结构的一个图片:

1、初始化

public HashMap(int initialCapacity, float loadFactor) { 有两个参数,一个初始值,一个加载因子,默认情况初始值是16,加载因子是0.75
       ........ 

        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;

        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor); 

        table = new Entry[capacity];
        init();
    }

2、扩容(重构)

 当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。所以为了提高查询的效率,就要对HashMap的数组进行扩容,数组扩容这个操作也会出现在ArrayList中,这是一个常用的操作,而在HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。

   那么HashMap什么时候进行扩容呢?当HashMap中的元素个数超过threshold(数组大小*loadFactor)时,就会进行数组扩容,loadFactor的默认值为0.75,这是一个折中的取值。也就是说,默认情况下,数组大小为16,那么当HashMap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。

 

3、解决冲突:在数据结构中有线性散列,平方散列等等方式:

hashmap源码采用的方式是:通过复杂的算法找到要插入的位置如下,如果已经有元素了,就线性的往后查找

hash值的算法:

 static int hash(int h) {
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

在数据结构教材上是直接除以一个数取余,而Java实现却如此复杂,网上查资料:

先看个例子,一个十进制数32768(二进制1000 0000 0000 0000),经过上述公式运算之后的结果是35080(二进制1000 1001 0000 1000)。看出来了吗?或许这样还看不出什么,再举个数字61440(二进制1111 0000 0000 0000),运算结果是65263(二进制1111 1110 1110 1111),现在应该很明显了,它的目的是让“1”变的均匀一点,散列的本意就是要尽量均匀分布。

 

将得到的值和(length-1)相与,得到的结果就是插入的索引位置

static int indexFor(int h, int length) {
        return h & (length-1);
    }

 

具体的插入方法:

 public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode()); 先得到hash值
        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;
    }

到此就解决了刚刚提出的问题:如果得到索引,如何解决冲突。

分享到:
评论

相关推荐

    hashMap1.8源码

    HashMap是Java编程语言中最常用的集合类之一,它提供了一种基于键值对(key-value pair)的数据存储方式,具有高效查找、插入和删除操作。...对于学习者来说,阅读源码并结合实践是掌握HashMap的最好方式。

    面试必考之HashMap源码分析与实现

    在Java编程语言中,HashMap是集合框架中一个重要的类,用于存储键值对的数据结构。面试中,HashMap的源码分析与实现是一个常见的考察点,...深入学习和实践HashMap源码,能够帮助我们更好地理解和优化Java应用程序。

    HashMap源码剖析共10页.pdf.zip

    《HashMap源码剖析》 HashMap是Java编程语言中一个非常重要的数据结构,它属于集合框架的一部分,提供了键值对(Key-Value)的存储方式。HashMap在内部使用了一个数组和链表来实现,实现了快速的查找、插入和删除...

    易语言源码易语言HashMap类源码.rar

    4. **解决哈希冲突**:哈希函数不可能总是产生唯一的索引,所以当两个键的哈希值相同时,易语言HashMap类需要有处理冲突的策略,如开放寻址法或链地址法。 5. **插入操作**:`Insert`方法用于添加新的键值对到...

    HashMap源码分析

    Java的HashMap使用链地址法处理冲突,即当多个键映射到同一个索引位置时,它们会在该位置形成一个链表。查找时,如果计算出的哈希值相同,则会遍历链表直到找到对应的键值对。 HashMap是非同步的,这意味着在多线程...

    HashMap 源码分析

    在JDK1.8之前,HashMap的底层数据结构采用的是数组+链表的方式,这种方式称为“拉链法”来解决哈希冲突。拉链法的基本思想是将相同哈希值的元素存储在一个链表中,通过数组索引来定位链表的头部。然而,HashMap与...

    学习笔记:三数组实现的HashMap

    - 在标准HashMap中,冲突通过开放寻址法或链地址法解决。三数组实现可能采用类似链地址法的方式,通过第三个数组的索引来处理哈希冲突。 - 当两个键的哈希值相同时,它们在第三个数组中存储相同的索引,然后可以...

    全手写HashMap精简版Demo 可直接允许查看效果

    在学习过程中,可以重点关注哈希函数的设计、链表的插入与查找以及扩容的实现,这些都是HashMap性能的关键因素。同时,它也可以作为进一步研究和优化哈希表的基础,例如探索更高效的哈希算法或者优化扩容策略。

    深入Java集合学习系列:HashMap的实现原理

    本文将深入探讨HashMap的内部机制,包括其构造、工作原理、哈希函数、冲突解决策略以及扩容机制。 首先,HashMap的基本结构是由数组(Entry[] table)和链表组成的。每个元素是一个内部类Entry,它包含了键值对...

    易语言HashMap类

    易语言HashMap类是一种在易语言编程环境中实现的高效数据结构,它主要用于存储键值对(key-value pairs),提供快速的数据存取。...通过深入学习和实践,开发者可以更好地利用HashMap类解决实际编程问题。

    hashmap.zip

    - "资料"可能包括HashMap的源码分析,揭示了HashMap如何处理哈希冲突,以及扩容的细节。 - "讲义"可能系统地介绍了HashMap的基础知识,如插入、查找和删除的步骤,以及HashMap与其他数据结构(如TreeMap)的区别。 ...

    HashMap资料.zip

    10. **面试常见问题**:面试中常问的问题包括HashMap的扩容机制、哈希冲突解决策略、线程安全性、迭代器的使用等,还可能涉及HashMap的源码分析。 通过深入学习和理解HashMap,不仅可以提高代码的编写效率,也能在...

    HashMap put方法的源码分析

    总结起来,理解HashMap的put方法源码对于优化Java程序中的数据处理至关重要,它揭示了HashMap如何高效地管理数据并处理冲突,这对于开发者来说是非常宝贵的编程知识。通过深入学习这些细节,我们可以更好地利用...

    易语言HashMap类源码-易语言

    通过分析源码,我们可以学习如何在易语言环境中实现高效的哈希表,这对于优化程序性能、解决实际问题有着重要的价值。此外,理解源码也有助于我们自定义和扩展数据结构,以适应特定的应用场景。

    20-集合框架020-HashMap-1080P 高清-AVC20

    通过视频资源“20-集合框架020-HashMap-1080P 高清-AVC.mp4”,你可以更直观地学习和掌握HashMap的详细知识。这个高清视频可能包含了HashMap的实例演示、源码分析以及常见问题解答,有助于深入理解和应用HashMap。

    java数据结构源码

    在Java编程语言中,数据结构是程序设计的基础,它涉及到如何高效地存储和组织数据,以便于执行各种操作。源码分析是理解这些数据结构工作原理的关键,...同时,源码学习也是提升编程技能和培养问题解决能力的重要途径。

    Hash(哈希)表详解示例

    由于没有详细内容,我们无法深入讨论,但可以推测这可能包含了哈希表的创建、插入、查找和冲突解决等基本操作的代码示例,或者是针对特定场景优化哈希表性能的方法。 总的来说,理解哈希表的工作原理和特性,掌握其...

    程序员实用算法的源码

    在实际应用中,散列表(HashMap)常用于存储键值对,提供了O(1)的平均查找时间。 其次,树是一种非线性数据结构,通常由n(n&gt;0)个有限节点组成,这些节点按照层次从上到下排列,最上面的节点称为根节点,除了根...

    java源码整理包-集合

    通过阅读这些源码,开发者可以深入了解Java集合框架的内部实现,包括如何处理哈希冲突、如何维护元素的排序、以及如何实现线程安全等核心概念。这对于提升编程技巧、优化代码以及解决实际问题具有重要意义。同时,这...

    java源码:哈希计算工具 java-hash.7z

    在Java编程领域,哈希计算工具是至关重要的,它们用于生成数据唯一标识,常用于数据校验、存储索引和快速查找。这个"java-hash.7z"压缩包包含了一个Java实现的哈希计算工具,这是一份经典的学习资源,可以帮助开发者...

Global site tag (gtag.js) - Google Analytics