HashMap原理

概述
HashMap基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。
值得注意的是HashMap不是线程安全的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap。( Map map = Collections.synchronizedMap(new HashMap());)

数据结构
HashMap的底层主要是基于数组和链表来实现的,它之所以有相当快的查询速度主要是因为它是通过计算散列码来决定存储的位置。HashMap中主要是通过key的hashCode来计算hash值的,只要hashCode相同,计算出来的hash值就一样。如果存储的对象对多了,就有可能不同的对象所算出来的hash值是相同的,这就出现了所谓的hash冲突。HashMap底层是通过链表来解决hash冲突的。

源码分析
HashMap类中的一些关键属性
transient Entry[] table;//存储元素的实体数组
transient int size;//存放元素的个数
int threshold; //临界值   当实际大小超过临界值时,会进行扩容threshold = 加载因子*容量
final float loadFactor; //加载因子
transient int modCount;//被修改的次数


加载因子越大,填满的元素越多,好处是,空间利用率高了,但:冲突的机会加大了.链表长度会越来越长,查找效率降低。反之,加载因子越小,填满的元素越少,好处是:冲突的机会减小了,但:空间浪费多了.表中的数据将过于稀疏(很多空间还没用,就开始扩容了)冲突的机会越大,则查找的成本越高.取默认值0.75。初始容量是16,容量为2的n次幂。

存储数据
put方法
若“key为null”,则将该键值对添加到table[0]中。
若“key不为null”,则计算该key的哈希值i,然后将其添加到该哈希值对应的链表中。
搜索指定hash值在对应table中的索引
循环遍历Entry数组,若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!
修改次数+1
将key-value添加到table[i]处
如果要加入的位置有值,将该位置原先的值设置为新entry的next,也就是新entry链表的下一个节点
get方法
首先计算key的hashCode,找到数组中对应位置的某一元素,
然后通过遍历key的equals方法在对应位置的链表中找到需要的元素。

HashMap的性能参数:
HashMap 包含如下几个构造器:
HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。
HashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。
HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。
HashMap的基础构造器HashMap(int initialCapacity, float loadFactor)带有两个参数,它们是初始容量initialCapacity和加载因子loadFactor。
initialCapacity:HashMap的最大容量,即为底层数组的长度。
loadFactor:负载因子loadFactor定义为:散列表的实际元素数目(n)/ 散列表的容量(m)。

Fail-Fast机制:
ava.util.HashMap不是线程安全的,如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略
这一策略在源码中的实现是通过modCount域,modCount就是修改次数,对HashMap内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。在迭代过程中,判断modCount跟expectedModCount是否相等,如果不相等就表示已经有其他线程修改了Map:modCount声明为volatile,保证线程之间修改的可见性。

猜你喜欢

转载自ziser.iteye.com/blog/2396892