之前看过一部分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;
}
到此就解决了刚刚提出的问题:如果得到索引,如何解决冲突。
分享到:
相关推荐
HashMap是Java编程语言中最常用的集合类之一,它提供了一种基于键值对(key-value pair)的数据存储方式,具有高效查找、插入和删除操作。...对于学习者来说,阅读源码并结合实践是掌握HashMap的最好方式。
在Java编程语言中,HashMap是集合框架中一个重要的类,用于存储键值对的数据结构。面试中,HashMap的源码分析与实现是一个常见的考察点,...深入学习和实践HashMap源码,能够帮助我们更好地理解和优化Java应用程序。
《HashMap源码剖析》 HashMap是Java编程语言中一个非常重要的数据结构,它属于集合框架的一部分,提供了键值对(Key-Value)的存储方式。HashMap在内部使用了一个数组和链表来实现,实现了快速的查找、插入和删除...
4. **解决哈希冲突**:哈希函数不可能总是产生唯一的索引,所以当两个键的哈希值相同时,易语言HashMap类需要有处理冲突的策略,如开放寻址法或链地址法。 5. **插入操作**:`Insert`方法用于添加新的键值对到...
Java的HashMap使用链地址法处理冲突,即当多个键映射到同一个索引位置时,它们会在该位置形成一个链表。查找时,如果计算出的哈希值相同,则会遍历链表直到找到对应的键值对。 HashMap是非同步的,这意味着在多线程...
在JDK1.8之前,HashMap的底层数据结构采用的是数组+链表的方式,这种方式称为“拉链法”来解决哈希冲突。拉链法的基本思想是将相同哈希值的元素存储在一个链表中,通过数组索引来定位链表的头部。然而,HashMap与...
- 在标准HashMap中,冲突通过开放寻址法或链地址法解决。三数组实现可能采用类似链地址法的方式,通过第三个数组的索引来处理哈希冲突。 - 当两个键的哈希值相同时,它们在第三个数组中存储相同的索引,然后可以...
在学习过程中,可以重点关注哈希函数的设计、链表的插入与查找以及扩容的实现,这些都是HashMap性能的关键因素。同时,它也可以作为进一步研究和优化哈希表的基础,例如探索更高效的哈希算法或者优化扩容策略。
本文将深入探讨HashMap的内部机制,包括其构造、工作原理、哈希函数、冲突解决策略以及扩容机制。 首先,HashMap的基本结构是由数组(Entry[] table)和链表组成的。每个元素是一个内部类Entry,它包含了键值对...
易语言HashMap类是一种在易语言编程环境中实现的高效数据结构,它主要用于存储键值对(key-value pairs),提供快速的数据存取。...通过深入学习和实践,开发者可以更好地利用HashMap类解决实际编程问题。
- "资料"可能包括HashMap的源码分析,揭示了HashMap如何处理哈希冲突,以及扩容的细节。 - "讲义"可能系统地介绍了HashMap的基础知识,如插入、查找和删除的步骤,以及HashMap与其他数据结构(如TreeMap)的区别。 ...
10. **面试常见问题**:面试中常问的问题包括HashMap的扩容机制、哈希冲突解决策略、线程安全性、迭代器的使用等,还可能涉及HashMap的源码分析。 通过深入学习和理解HashMap,不仅可以提高代码的编写效率,也能在...
总结起来,理解HashMap的put方法源码对于优化Java程序中的数据处理至关重要,它揭示了HashMap如何高效地管理数据并处理冲突,这对于开发者来说是非常宝贵的编程知识。通过深入学习这些细节,我们可以更好地利用...
通过分析源码,我们可以学习如何在易语言环境中实现高效的哈希表,这对于优化程序性能、解决实际问题有着重要的价值。此外,理解源码也有助于我们自定义和扩展数据结构,以适应特定的应用场景。
通过视频资源“20-集合框架020-HashMap-1080P 高清-AVC.mp4”,你可以更直观地学习和掌握HashMap的详细知识。这个高清视频可能包含了HashMap的实例演示、源码分析以及常见问题解答,有助于深入理解和应用HashMap。
在Java编程语言中,数据结构是程序设计的基础,它涉及到如何高效地存储和组织数据,以便于执行各种操作。源码分析是理解这些数据结构工作原理的关键,...同时,源码学习也是提升编程技能和培养问题解决能力的重要途径。
由于没有详细内容,我们无法深入讨论,但可以推测这可能包含了哈希表的创建、插入、查找和冲突解决等基本操作的代码示例,或者是针对特定场景优化哈希表性能的方法。 总的来说,理解哈希表的工作原理和特性,掌握其...
在实际应用中,散列表(HashMap)常用于存储键值对,提供了O(1)的平均查找时间。 其次,树是一种非线性数据结构,通常由n(n>0)个有限节点组成,这些节点按照层次从上到下排列,最上面的节点称为根节点,除了根...
通过阅读这些源码,开发者可以深入了解Java集合框架的内部实现,包括如何处理哈希冲突、如何维护元素的排序、以及如何实现线程安全等核心概念。这对于提升编程技巧、优化代码以及解决实际问题具有重要意义。同时,这...
在Java编程领域,哈希计算工具是至关重要的,它们用于生成数据唯一标识,常用于数据校验、存储索引和快速查找。这个"java-hash.7z"压缩包包含了一个Java实现的哈希计算工具,这是一份经典的学习资源,可以帮助开发者...