关于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 在查看一下定位一下第几行

posted @ 2020-09-20 18:25  九角冰山  阅读(314)  评论(0编辑  收藏  举报