HashMap原理,Hash冲突,同步集合和并发集合及实现原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/donghejk/article/details/83781932
  1. HashMap的原理

通过hash的方法,通过put和get存储和获取对象。

存储对象时:我们将K/V传给put方法时,它调用hashCode计算hash从而得到bucket位置,进一步存储,HashMap会根据当前bucket的占用情况自动调整容量(超过LoadFacotr则resize为原来的2倍)。
获取对象时:我们将K传给get,它调用hashCode计算hash从而得到bucket位置,并进一步调用equals()方法确定键值对。
发生碰撞的时候:Hashmap通过链表将产生碰撞冲突的元素组织起来,在Java8中,如果一个bucket中碰撞冲突的元素超过某个限制(默认是8),则使用红黑树来替换链表,从而提高速度。

参数说明: 简单的说,Capacity就是buckets的数目,Loadfactor就是buckets填满程度的最大比例。如果对迭代性能要求很高的话不要把capacity设置过大,也不要把loadfactor设置过小。当bucket填充的数目(即hashmap中元素的个数)大于capacity*loadfactor时就需要调整buckets的数目为当前的2倍。

  1. Hash冲突

通过哈希函数把键映射成索引,而在多个键映射的索引值可能相同,这就是Hash冲突也叫哈希碰撞。

哈希冲突解决办法

  • 拉链法 (Separate chaining with linked lists)

通过哈希函数,我们可以将键转换为数组的索引(0-M-1),但是对于两个或者多个键具有相同索引值的情况,我们需要有一种方法来处理这种冲突。

一种比较直接的办法就是,将大小为M 的数组的每一个元素指向一个条链表,链表中的每一个节点都存储散列值为该索引的键值对,这就是拉链法。下图很清楚的描述了什么是拉链法。
在这里插入图片描述
seperate chaining with link list

图中,”John Smith”和”Sandra Dee” 通过哈希函数都指向了152 这个索引,该索引又指向了一个链表, 在链表中依次存储了这两个字符串。

该方法的基本思想就是选择足够大的M,使得所有的链表都尽可能的短小,以保证查找的效率。对采用拉链法的哈希实现的查找分为两步,首先是根据散列值找到等一应的链表,然后沿着链表顺序找到相应的键。 我们现在使用我们之前介绍符号表中的使用无序链表实现的查找表SequentSearchSymbolTable 来实现我们这里的哈希表。

  • 线性探测法

线性探测法是开放寻址法解决哈希冲突的一种方法,基本原理为,使用大小为M的数组来保存N个键值对,其中M>N,我们需要使用数组中的空位解决碰撞冲突。如下图所示:

在这里插入图片描述

对照前面的拉链法,在该图中,”Ted Baker” 是有唯一的哈希值153的,但是由于153被”Sandra Dee”占用了。而原先”Snadra Dee”和”John Smith”的哈希值都是152的,但是在对”Sandra Dee”进行哈希的时候发现152已经被占用了,所以往下找发现153没有被占用,所以存放在153上,然后”Ted Baker”哈希到153上,发现已经被占用了,所以往下找,发现154没有被占用,所以值存到了154上。

开放寻址法中最简单的是线性探测法:当碰撞发生时即一个键的散列值被另外一个键占用时,直接检查散列表中的下一个位置即将索引值加1,这样的线性探测会出现三种结果:

命中,该位置的键和被查找的键相同
未命中,键为空
继续查找,该位置和键被查找的键不同。

  • 红黑树

留空

  1. 同步集合

Hashtable
Vector
同步集合包装类,Collections.synchronizedMap()和Collections.synchronizedList()

  1. 并发集合
    ArrayList和HashMap是非并发集合,迭代时不能进行修改和删除操作。于是1.5之后出现了并发了集合
    并发集合类主要有:
    ConcurrentHashMap; ConcurrentSkipListMap; ConCurrentSkipListSet; CopyOnWriteArrayList; CopyOnWriteArraySet; ConcurrentLinkedQueue;

ConcurrentHashMap 线程安全,内部是部分锁定。

ConcurrentHashMap支持完全并发的检索和更新所希望的可调整并发的哈希表。此类遵守与 Hashtable 相同的功能规范,并且包括对应于 Hashtable 的每个方法的方法版本。不过,尽管所有操作都是线程安全的,但检索操作不 必锁定,并且不 支持以某种防止所有访问的方式锁定整个表。此类可以通过程序完全与 Hashtable 进行互操作,这取决于其线程安全,而与其同步细节无关。
主要构造方法有:

ConcurrentHashMap(); //创建一个带有默认初始容量、加载因子和 concurrencyLevel 的新的空映射。
ConcurrentHashMap(int initialCapacity); //创建一个带有指定初始容量、默认加载因子和 concurrencyLevel 的新的空映射。
ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel); //创建一个带有指定初始容量、加载因子和并发级别的新的空映射。
ConcurrentHashMap(Map<> t); //构造一个与给定映射具有相同映射关系的新映射。

ConcurrentLinkedQueue 非阻塞,内部用的不是锁,使用wait-free”算法来实现

ConcurrentLinkedQueue是一个基于链接节点的、无界的、线程安全的队列。此队列按照 FIFO(先进先出)原则对元素进行排序,队列的头部 是队列中时间最长的元素。队列的尾部 是队列中时间最短的元素。新的元素插入到队列的尾部,队列检索操作从队列头部获得元素。当许多线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择,此队列不允许 null 元素。
主要构造方法有:
[java]
ConcurrentLinkedQueue(); //创建一个最初为空的 ConcurrentLinkedQueue。
ConcurrentLinkedQueue(Collection<> c);// 创建一个最初包含给定 collection 的元素的 ConcurrentLinkedQueue,按照此 collection 迭代器的遍历顺序来添加元素

CopyOnWriteArrayList

CopyOnWriteArrayList是ArrayList 的一个线程安全的变形,其中所有可变操作(添加、设置,等等)都是通过对基础数组进行一次新的复制来实现的。这一般需要很大的开销,但是当遍历操作的数量大大超过可变操作的数量时,这种方法可能比其他替代方法更 有效。在不能或不想进行同步遍历,但又需要从并发线程中排除冲突时,它也很有用。“快照”风格的迭代器方法在创建迭代器时使用了对数组状态的引用。此数组在迭代器的生存期内绝不会更改,因此不可能发生冲突,并且迭代器保证不会抛出 ConcurrentModificationException。自创建迭代器以后,迭代器就不会反映列表的添加、移除或者更改。不支持迭代器上更改元素的操作(移除、设置和添加)。这些方法将抛出 UnsupportedOperationException。
主要构造方法有:
[java]
CopyOnWriteArrayList(); // 创建一个空列表。
CopyOnWriteArrayList(Collection<> c); //按照 Collection 的迭代器返回元素的顺序,创建一个包含指定 Collection 的元素的列表。
CopyOnWriteArrayList(E[] toCopyIn); //创建一个新的 CopyOnWriteArrayList,它保持给定数组的副本。

CopyOnWriteArraySet

CopyOnWriteArraySet是对其所有操作使用 CopyOnWriteArrayList 的 Set。因此,它共享以下相同的基本属性:
它最适合于 set 大小通常保持很小、只读操作远多于可变操作以及需要在遍历期间防止线程间冲突的应用程序。
它是线程安全的。
因为通常需要复制整个基础数组,所以可变操作(添加、设置、移除,等等)的开销巨大。
迭代器不支持可变移除操作。
使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。
主要构造方法包括:
[java]
CopyOnWriteArraySet(); //创建一个空 set
CopyOnWriteArraySet(Collection<> c); //创建一个包含指定 Collection 中所有元素的 set

  1. 线程安全及实现原理

参考文章:
http://www.cnblogs.com/yangecnu/p/Introduce-Hashtable.html
https://yikun.github.io/2015/04/01/Java-HashMap工作原理及实现/
https://blog.csdn.net/bigtree_3721/article/details/51303914

猜你喜欢

转载自blog.csdn.net/donghejk/article/details/83781932