产品经理都看懂了之hashmap、hashtable、ConcurrentHashMap解析

一、什么是hash

既然每个字都离不开hash,那我们就从hash函数说起:hash函数也称散列函数、杂凑函数(很形象了),他的作用一般用于信息安全中的加密算法,具体做法是把任意长度的输入(又叫做预映射)通过hash(散列)算法,变成固定长度的输出,一般情况下散列空间远小于输入空间,但是不同的输入可能造成相同的输出(哈希碰撞,一个关键字会映射到同一个位桶中的情况,这种情况就就叫做哈希冲突,解决方案之一就是拉链法hashmap使用的方法,再哈希、开放地址等等),所以不能从散列值倒推得到输入值。

以上描述中桶的概念:哈希桶就是盛放不同key链表的容器,我们可以把每个key的位置看作是一个指针,该指针所指向的位置里放了一个链表,可以认为是指针数组。思考:redis集群做负载均衡时使用的hash solt是一个原理

简单地说哈希算法的实质是对原始数据的有损压缩,有损压缩后的固定字长用作唯一标识原始数据。我们熟知的MD5就是基于hash算法,当然MD5可以使用彩虹表破解(加盐是固定值),而BCrypt加密算法加盐是随机值,每次产生加密输出都不同(笔者已测试,但是如何进行登陆匹配还有待解决)

二、什么是hash表

哈希表,又称为散列,是一种更加快捷的查找技术。我们之前的查找,都是这样一种思路:集合中拿出来一个元素,看看是否与我们要找的相等,如果不等,缩小范围,继续查找。而哈希表是完全另外一种思路:当我知道key值以后,我就可以直接计算出这个元素在集合中的位置,根本不需要一次又一次的查找!

哈希表属于一种物理存储结构(物理存储结构:顺序、链式、索引、散列)。采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表(Hash table),也就是说:他使用了hash算法,所以就叫他哈希表。

            物理存储结构:
            顺序、链式:共同特征是元素之间都有映射关系
            hash表:(散列存储结构)的元素之间相互独立
            索引:类似与字典目录

先了解几个概念:

    capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。
    loadFactor:负载因子,默认为 0.75。
    threshold:扩容的阈值,等于 capacity * loadFactor

hashMap:我们介绍java8的hashmap,与Java7中数组加链表的数据结构(先hash()得到数组角标,然后遍历链表所以时间复杂度取决于链表的长度O(n))不同,java8的hashmap改进了这中数据结构,当链表的长度超过8之后,会将链表转化成红黑树,时间复杂度降为了O(logN),唠叨一句

先理解两个概念:
1、整个 ConcurrentHashMap 由一个个 Segment 组成,Segment 代表”部分“或”一段“的意思,所以很多地方都会将其描述为分段锁。注意,行文中,我很多地方用了“槽”来代表一个 segment。
2、concurrencyLevel,并行级别、并发数、Segment 数,怎么翻译不重要,理解它。默认是 16,也就是说 ConcurrentHashMap 有 16 个 Segments,所以理论上,这个时候,最多可以同时支持 16 个线程并发写,只要它们的操作分别分布在不同的 Segment 上。这个值可以在初始化的时候设置为其他值,但是一旦初始化以后,它是不可以扩容的。

ConcurrentHashMap:先说java7中ConcurrentHashMap,它的和java7的HashMap基本一样,不过他要保证线程安全,实现会复杂一些。大体实现:加锁时利用更细粒度的锁,使用lock,在默认的16个segment槽,也有人称之为桶,put的时候需要锁住segment但是get的时候不要加锁//todo

而java8中摈弃了segment的概念,启用一种全新的方式,实现全新的实现方式,利用CAS算法。它沿用了与它同时期的HashMap版本的思想,底层依然由“数组”+链表+红黑树的方式思想(JDK7与JDK8中HashMap的实现),但是为了做到并发,又增加了很多辅助的类,比如TreeBin、Traverser等对象内部类。

java8中,不采用segment而采用node,锁住node来实现减小锁粒度。
设计了MOVED状态 当resize的中过程中 线程2还在put数据,线程2会帮助resize。
使用3个CAS操作来确保node的一些操作的原子性,这种方式代替了锁。
sizeCtl的不同值来代表不同含义,起到了控制的作用。

hashtable:Hashtable 的函数都是同步的,这意味着它是线程安全的,但是效率很低。很好理解,注意它的key、value都不可以为null。

猜你喜欢

转载自blog.csdn.net/SmartShylyBoy/article/details/82054835
今日推荐