zoukankan      html  css  js  c++  java
  • 关于HashMap的理解

    HashMap 的底层数据结构是什么?

    jdk1.7 是 数组 + 链表,jdk1.8 是 数组 + 链表 + 红黑树。

    ( 0.75的 local factors 加载因子 数组长度64 链表长度8 超过后调用红黑树)

    (1.7是一个个都是Node<>节点,1.8以后都是Entry<>节点)

    (1.7 头插 ;1.8 尾插) 头插尾插区别就是 扩容前hashmap A 扩容后 不再是之前的那个A 了链表的顺序位置可能已经变了

    Hash冲突 ?

    先捋一下hash ,哈希是通过数据进行再压缩,提高效率的一种手段。但由于通过哈希函数产生的哈希哈希值是有限的,导致经过哈希函数处理后仍然有不同的数据对应相同的值, 这种现象就是 Hash冲突。

    而在这种现象多出现在 add/put 步骤中,初步计算插入的object计算哈希值

    产生哈希冲突的影响因素

    装填因子(装填因子 = 数据总数 / 哈希表长)、哈希函数、处理冲突的方法

    解决哈希冲突的四种方法

    •  开放抵制方法
      
    •  链式地址法
      
    •  建立公共溢出区
      
    •  再哈希法
      

    hashmap中索引的运算流程

    hashmap中的数组索引下表是怎么来的 加入一个 A 元素,取这个A元素的hashCode 与A元素高八位进行异或
    也就是 A.index = A.hashCode() ^ ( A.hashCode() >>> 16), 优化得 A.index = (n - 1) & A.hashCode()
    此处的16也就是现有hashmap数组的长度 和 n 所代表的的一样

    这也是以上hashmap为什么2倍扩容的原因

    在hashmap resize的时候要进行位运算,在每一次putValue,当扩容以后仍要数组长度为 2的整数倍,在计算数组索引下标时仍采用异或运算,个人理解为是为了迎合hashmap本身的算法罢了。

    那假如说我加入一个键值对,这个时候出现了冲突,它只怎么把这个节点加入进去?是加入到当前bucket所对应的链表的头结点还是尾节点?(答不上来可以问一个稍微简单的,equals和 == 的区别,以及其中hashCode的作用)

    想想啊, 组织一下语言,当一个元素通过hashcode 对长度取模一系列操作后得到自己的索引位置,如果此时索引位置没有其他元素,则直接进行存储,如果已有其他元素那么会进行equals方法(等会在带一嘴 = =和equals吧),如果equals放回true则视为重复元素,如果false则允许添加。
    (== 比的是 内存地址,equals 比的是 值,当然不同元素会有不同,如Integer 好像是 integer类型 -128 —— 127 在加载integer类时,JVM会进行一步缓存,此时内存在射程范围内的内置对比的都是缓存的地址)

    再再提一嘴 hashCode + 异或运算这种操作其实就是为了 解决我一个元素put进hashmap时对于存储位置的有效分配手段。 幼儿园 :充分提高散列的粒度

    可以说说什么条件下,可以把一个链表转成红黑树呢?它里面的大概流程是什么,了解吗?

    条件:链表超度超过 8时,链表则变为红黑树
    为什么是8? (泊松分布)

    在高并发大流量的情况下,hashMap有什么问题吗,会不会造成 cpu达到100% ?如果会,那是在哪一步可能会出现这个问题呢(插入、删除、查找、扩容)?

    高并发情况下,由于自身的线程不安全,多线程 hashmap 在插入过程中可能会造成自身死锁现象,(推荐使用concurrenthashmap 有锁)

    病因:

    hash表的初始大小(16)有限,当put存入的数据增加时就必须扩容,源码中会调用rehash方法(即是把原表内容移动到一个新的表中 copy),单线程下rehash就是把原来链表遍历,从新的链表头部挨个放入(为什么不加到新链表最后? 因为复杂度为 O(N))放入的过程中一次执行函数transfer();

    多线程的时候会出现同时put()操作,并进入了 transfer环节
    假设现在有三个线程:thread1,thread2,thread3;
    在老数组中的第一个数据也就是e引用的对象,我们称它为A,在新数组中的头两个数据分别为B和C,B.next=C。 线程thread1运行到e.next=newTable[i]时 thread2 运行到了 next=e.next; 然后thread1继续执行
    会产生什么效果? A->B B->C
    多线程 add/put插入时 第三个线程thread3 如果在thread1执行 e.next = newTable[i]的时候
    去执行next = e.next,那么就中途改变了next的值,本来是保存C的,但是现在成了A了,总结下现在的情况:
    e引用A,nextTable[i]引用B,A.next=B,B.next=A。 然后就死锁了

    (CPU占用过高)怎么办 【遇到过一次死锁,惯例提一嘴】

    分析通过jdk命令和linux命令进行定位
    top 看一下后台系统的cpu占用情况(找到CPU占用率最高的进程) —— ps -ef 或者 jps -l(锁定进程)
    ps -mp 进程号 (锁定线程号) —— 然后将锁定好的线程号转为16进制 —— 之后jstack 线程号 -A60 在查看一下定位一下第几行

  • 相关阅读:
    【软件教程】oracle11g数据库的下载和安装
    (一)最新VMware vSphere Data Protection(VDP) 6.1.11 的安装过程
    缓存与库先写哪个,这十几张图告诉你
    因为它,差点无缘大厂梦!!!
    从小公司进入大厂,我都做对了哪些事?
    毕业一年后接私活赚了10w,还拿了几家大厂offer!
    同样是持久化,竟然有这么大的差别!
    硬核!15张图解Redis为什么这么快
    面试时说Redis是单线程的,被喷惨了!
    【漫画】活见鬼,明明删除了数据,空间却没减少!
  • 原文地址:https://www.cnblogs.com/nineberg/p/13693221.html
Copyright © 2011-2022 走看看