什么是hashcode
分析HashMap之前先介绍下什么Hashcode(散列码)。它是一个int,每个对象都会有一个hashcode,它在内存的存放位置是放在对象的
头部(对象头部存放的信息有hashcode,指向Class的引用,和一些有关垃圾回收信息)。具体如何生成hashcode,这个相当复杂,由于我们
的主题是“浅析”,所以不深入探讨。有个问题需要讲的是,如果在你的类中覆盖了Object的equals(Object)方法,那么你必须覆盖
hashCode方法,不然,当你使用HashMap,HashSet,HashTable时会出现问题。具体原因下文会详细描述。
String类的hashcode
String类重写了Object类中的equals和hashCode方法,原因很简单,Object中的equals方法是指比较两个对象是不是指向
同一个引用对象,而String类指需要比较内容相不相等就可以了。所以String覆盖了equals方法,同时覆盖了hashCode方法。这里需要
提一下Object规范里规定:如果两个对象根据equals(Object)方式是相等的,那么这两个对象的hashCode一定要相等。
String中的hashcode算法很简单如下:
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
Val是一个char[],存放的是字符串的各个字符;Len是字符串长度;off从0开始;h从0开始。比如一个字符串“abc”(a的ascii码是97),它的hashcode算法是:
h = 31 * 0 + 97 ==> h = 97;
h = 31 * 97 + 98 ==> h = 3105;
h = 31 * 3105 + 99 ==> h = 96354;
所以“abc”的hashCode就是96354
存储方式
列表的存储方式是基于数组,如下图:
链表的存储方式是基于指针,如下图:(单向链表)
HashMap的存储方式是上面两种的结合,如下图:
HashMap的存取
HashMap的功能是通过“键(key)”能够快速的找到“值”。下面我们分析下HashMap存数据的基本流程:
1、 当调用put(key,value)时,首先获取key的hashcode,int hash = key.hashCode();
2、 再把hash通过一下运算得到一个int h.
hash ^= (hash >>> 20) ^ (hash >>> 12);
int h = hash ^ (hash >>> 7) ^ (hash >>> 4);
为什么要经过这样的运算呢?这就是HashMap的高明之处。先看个例子,一个十进制数32768(二进制1000 0000 0000
0000),经过上述公式运算之后的结果是35080(二进制1000 1001 0000
1000)。看出来了吗?或许这样还看不出什么,再举个数字61440(二进制1111 0000 0000
0000),运算结果是65263(二进制1111 1110 1110
1111),现在应该很明显了,它的目的是让“1”变的均匀一点,散列的本意就是要尽量均匀分布。那这样有什么意义呢?看第3步。
3、
得到h之后,把h与HashMap的承载量(HashMap的默认承载量length是16,可以自动变长。在构造HashMap的时候也可以指定一个长
度。这个承载量就是上图所描述的数组的长度。)进行逻辑与运算,即 h &
(length-1),这样得到的结果就是一个比length小的正数,我们把这个值叫做index。其实这个index就是索引将要插入的值在数组中的
位置。第2步那个算法的意义就是希望能够得出均匀的index,这是HashTable的改进,HashTable中的算法只是把key的
hashcode与length相除取余,即hash %
length,这样有可能会造成index分布不均匀。还有一点需要说明,HashMap的键可以为null,它的值是放在数组的第一个位置。
4、
我们用table[index]表示已经找到的元素需要存储的位置。先判断该位置上有没有元素(这个元素是HashMap内部定义的一个类Entity,
基本结构它包含三个类,key,value和指向下一个Entity的next),没有的话就创建一个Entity<K,V>对象,在
table[index]位置上插入,这样插入结束;如果有的话,通过链表的遍历方式去逐个遍历,看看有没有已经存在的key,有的话用新的value替
换老的value;如果没有,则在table[index]插入该Entity,把原来在table[index]位置上的Entity赋值给新的
Entity的next,这样插入结束。
再回头看看前面提到的为什么覆盖了equals方法之后一定要覆盖hashCode方法,很简单,比如,String a = new
String(“abc”);String b = new
String(“abc”);如果不覆盖hashCode的话,那么a和b的hashCode就会不同,把这两个类当做key存到HashMap中的话就
会出现问题,就会和key的唯一性相矛盾。
HashMap取的过程也类似。太累了,不写了。赶紧结束。
文中可能会有错误的地方请指出,谢谢!
转自:http://www.iteye.com/topic/838030
分享到:
相关推荐
比较分析Vector、ArrayList和hashtable hashmap数据结构
HashMap是Java编程语言中一个非常重要的数据结构,它属于集合框架的一部分,主要用于存储键值对(Key-Value)数据。HashMap在内部实现上基于哈希表,也称为散列表,它提供了一种快速查找、插入和删除数据的方法,...
javaScript模拟的HashMap数据结构,可以方便的put和get。几乎和Java中HashMap类的功能一模一样。非常好用的!
在Java编程中,`HashMap`与`HashTable`作为两种常用的数据结构,经常被用来存储键值对数据。尽管它们在功能上相似,但在实现细节、性能表现以及使用场景方面存在显著差异。本文将深入探讨两者之间的区别,帮助读者更...
HashMap是一种高效的数据结构,它可以快速根据Key找到元素,但是需要正确地实现hashCode和equals方法,以避免哈希碰撞问题和equals方法的调用问题。 知识点: 1. 哈希函数的原理和应用 2. HashMap的存储原理和查询...
哈希映射(HashMap)是Java编程语言中广泛使用的数据结构之一,主要提供键值对的存储和查找功能。HashMap的实现基于哈希表的概念,它通过计算对象的哈希码来快速定位数据,从而实现了O(1)的平均时间复杂度。在深入...
在使用Java集合类时,如HashSet或HashMap,`equals()` 和 `hashCode()` 的一致性非常重要。如果你只重写了 `equals()` 而不重写 `hashCode()`,可能会导致无法正确地将对象添加到集合或从集合中查找对象。例如,在...
在Java集合框架中,特别是`HashSet`、`HashMap`等基于哈希表的数据结构中,`hashCode()`扮演着至关重要的角色。 **1.1 `hashCode()`的作用** - **定位对象**: 当一个对象被插入到哈希表中时,`hashCode()`可以帮助...
hashCode 是一种用于查找和排序的机制,在数据结构中 plays a crucial role。下面我们将对 hashCode 的用法进行详细的解释。 hashCode 的作用 hashCode 的主要作用是用于查找和排序。在查找和排序的过程中,我们...
hashMap存储分析hashMap存储分析
HashMap的一个数据结构 锁升级:锁升级过程 resize的过程在开发中 怎么保证容器它线程安全后就是数据插入过程使用的头插法 但是头插法会造成一些问题等等等等的那个等等的那个等等的那个等等的那个等等的那个等等的...
HashMap是一个基于哈希表实现的键值对存储结构,它提供了快速的插入、删除和查找操作,平均时间复杂度为O(1)。HashMap非线程安全,适合于单线程环境或已经通过并发工具类控制并发的场景。 二、HashMap底层原理 ...
- 这些数据结构利用 `HashCode` 来确定元素存储的位置,并通过索引来快速查找元素。 3. **Hashtable 的特殊处理**: - `Hashtable` 在计算 `HashCode` 时,会将 `HashCode` 的值与 `0x7FFFFFFF` 进行按位与操作,...
Java集合专题总结:HashMap和HashTable源码...本文总结了HashMap和HashTable的源码学习和面试总结,涵盖了它们的存储结构、构造方法、get和put方法的源码分析、Hash表的特点和缺点、HashTable和HashMap的区别等内容。
下面我们将深入探讨 HashMap 的数据结构、 put 方法的实现细节和 Hash 码的计算过程。 HashMap 的数据结构 HashMap 的数据结构可以分为两部分:数组和链表。数组是 HashMap 的基本结构,链表是数组元素的具体实现...
本文介绍了Java语言不直接支持关联数组,可以使用任何对象作为一个索引的数组,但在根Object类中使用 hashCode()方法明确表示期望广泛使用HashMap。理想情况下基于散列的容器提供有效插入和有效检索;直接在对象模式...
HashMap通过数组和链表(或红黑树)的结合来实现高效的键值对存储和查找。合理的哈希算法和适当的数组大小对于HashMap的性能至关重要。此外,HashMap还提供了自动扩容机制来应对不断增长的数据量,确保了在大多数...
Java中的HashMap是一个非常重要的数据结构,它实现了Map接口,提供了键值对的高效存储和访问。HashMap基于哈希表(也称为散列表)原理工作,它允许用户通过键(Key)快速查找对应的值(Value)。在HashMap中,键和值...
HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,虽然它们实现的接口规范不同,但它们底层的 Hash 存储机制完全一样。甚至 HashSet 本身就采用 HashMap 来实现的。 2. Hash 存储机制 HashMap ...