HashMap源码和常见问题分析以及多线程开发时的问题

来源:互联网 发布:张学友唱功 知乎 编辑:程序博客网 时间:2024/06/01 14:09

源码和常见问题分析

HashMap中存在两个关键方法:put(Object key,Object value)和get(Object key)下面我们围绕这俩个方法进行介绍。
传入的key NOT NULL情况:
首先先计算出Object key的hash值然后找到对应Bucket(一个储存Entry的数组,Entrty实际上是一个链表,包含之前Key的hash值、key、value以及指向下一个Entry的引用,而bucket存的是此链表的头部)的位置,然后遍历此Entrty列表。如果存在传入的Object key与Entry中存储的key的hash值相同并且相等(引用相等或者是equles相等),那么此bucket的value将会指向传入的Object value,也就是将newvalue替换oldvalue,同时返回oldvalue。如果不存在那么首先会判断HashMap当前的bucket数是否大于尺寸*负载因子(初始尺寸为16,负载因子通常情况为0.75),若大于那么将会扩容,然后创建一个Entry,把hash、key、value、下一个Entry等值传入,同时让对应的bucket(也就是存贮entry链表头部的那个数组)指向Entry,而下一个Entry则为原来的bucket,也就是将新建的Entry作为新链表的头部。这里写图片描述
传入的key NULL的情况:
key为null和not null情况大致相同,只是hashmap将key=null时的key的hash值定为0,而bucket位置也为0,也就是bucket[0]。
当发生hash碰撞冲突时
发生hash碰撞冲突的前提是key的hash值相同但是key不相等,当这种情况发生时,先找到hash对应的bucket位置,而在bucket位置已有一个不为空的Entry,所以新的Entry将会放在此链表头部,而原来的Entry将会放在下一个节点,然后get(Object key)方法进行调取时会调用equles方法对Entry的key进行判断,从而确保取出的value正确,hash碰撞冲突是会影响效率的,所以建议以String,int这样的类型作为key最佳,或者是在类中重写equles和hashCode方法。

多线程开发时的问题

HashMap的缺点:
HashMap并不适合多线程开发环境(被多个线程调用),在多线程环境下HashMap不仅仅在存入数据时会发生混乱,在HashMap扩容时多个线程可能会同时判定HashMap需要扩容而给他进行扩容,从而导致HashMap死循环。解决方法:使用Collections.synchronizedCollection(map)对HashMap进行包装(基于synchronized)、使用java.util.concurrent下的ConcurrentHashMap、使用HashTable。
HashMap与HashTable的区别:
不同点:
HashMap:快速失败,key可以为null,单线程环境比HashTable效率高很多。
HashTable:安全失败,key不能为null,单线程效率极低,但是是线程安全的集合
相同点:
俩者都是基于hash表,都是实现Map接口,几乎可以等价

原创粉丝点击