hashmap1.8优化后的区别 以及和ConcurrentHashMap 的区别

hashmap 

 时间复杂度空间复杂度
数组对于指定下标的查找,时间复杂度为O(1);通过给定值进行查找,需要遍历数组,逐一比对给定关键字和数组元素,时间复杂度为O(n),当然,对于有序数组,则可采用二分查找,插值查找,斐波那契查找等方式,可将查找复杂度提高为O(logn);对于一般的插入删除操作,涉及到数组元素的移动,其平均复杂度也为O(n) 
线性链表对于链表的新增,删除等操作(在找到指定操作位置后),仅需处理结点间的引用即可,时间复杂度为O(1),而查找操作需要遍历链表逐一进行比对,复杂度为O(n) 
二叉树O(logn) 
哈希表O(1) 

使用了链地址法,也就是数组+链表

HashMap的实现原理

主干是一个Entry数组

图片来源(6条消息) Java中HashMap底层实现原理(JDK1.8)源码分析_tuke_tuke的博客-CSDN博客_hashmap底层实现原理

 

HashMap底层实现原理

1.8之前

数组+链表

hahs冲突后使用头插法

1.8之后

数组+链表+红黑树

当发生hash冲突时,插入元素是尾插法,避免了

当链表长度长于一定值(默认为8)时,会转换成红黑树

红黑树的特性:  (6条消息) HashMap底层实现原理解析_苟且偷生的程序员的博客-CSDN博客_hashmap底层实现原理

在这里插入图片描述

1、每个节点要么是红色,要么是黑色,但根节点永远是黑色的;

2、每个红色节点的两个子节点一定都是黑色;

3、红色节点不能连续(也即是,红色节点的孩子和父亲都不能是红色);

4、从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点;

5、所有的叶节点都是是黑色的(注意这里说叶子节点其实是上图中的 NIL 节点);

为什么Hashmap的链表长度是16或者2的幂  比如15 是1111,要插入的值与它进行按位与,可以让所有的index值结果都可以出现,假如是1001的话,可能就有的值根本不会用到,不符合hash算法均匀分布的原则

 

ConcurrentHashMap 和 HashMap的区别

HashMap是线程不安全的

ConcurrentHashMap是线程安全的

JDK1.7 数据结构是有一个Segment数组和多个HashEntry组成,如图 来自 多线程-ConcurrentHashMap(JDK1.8) - 小路不懂2 - 博客园 (cnblogs.com)

 

Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,

每一个segment存储的是HashEntry数组+链表 .分多少段是开始就确定的,后期不能在扩容,也就是并发度不能改变,但是单个segment里面的数组是可以扩容的

put   是要进行两次hash去定位数据的存储位置

Segment实现了ReentrantLock,第一次先key的hash来定位segment的位置,如果该segment还没初始化,就通过CAS操作进行赋值,然后进行第二次hash操作找到entry位置,通过ReentrantLock的tryLock()方法去获取锁,然后插入相应位置,如果锁被其他线程获取,当前线程通过自旋方式去继续调用tryLock()去获取锁,超过指定次数就挂起,等待唤醒

get   第一次先定位到segment的位置,然后再hash定位到hashEntry位置,遍历该HashEntry下的链表进行对比

size  计算长度

try {
    for (;;) {
        if (retries++ == RETRIES_BEFORE_LOCK) {
            for (int j = 0; j < segments.length; ++j) ensureSegment(j).lock(); // force creation
        }
        sum = 0L;
        size = 0;
        overflow = false;
        for (int j = 0; j < segments.length; ++j) {
            Segment<K,V> seg = segmentAt(segments, j);
            if (seg != null) { sum += seg.modCount; int c = seg.count; if (c < 0 || (size += c) < 0)
               overflow = true;
            } }
        if (sum == last) break;
        last = sum; } }
finally {
    if (retries > RETRIES_BEFORE_LOCK) {
        for (int j = 0; j < segments.length; ++j)
            segmentAt(segments, j).unlock();
    }
}

也就是说,1.先通过不加锁的方式去多次获得一下size,最多三次,比较一致就认为没有元素加入,计算结果准确

2.如果不一致,就会给每个Segment加上锁,然后计算size返回

JDK1.8 

不再使用segment ,而是Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作.后续并发度大小依赖于table数组的大小

Node  是ConcurrentHashMap存储结构的基本单元,实现了Map.Entry接口,用于存储数据,其中的value和next属性设置了volatile同步锁,不允许调用setValue直接改变Node的value,增加了find方法辅助map.get()

TreeNode 继承于Node ,实现红黑树

TreeBin  从字面可以理解为存储树形结构的容器

ForwardingNode 一个用于链接2个table的节点类

JDK1.8中也是锁分离思想,只是锁住一个Node,而不是JDK7中的Segment

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值