Java基础:关于hashmap的问题

关于hashmap的问题

    • HashMap知识点
    • Java中的HashMap、LinkedMap、TreeMap解释下?
    • hashmap为什么线程不安全?
    • hashmap怎么解决hash冲突
    • 两个hashmap相比较的时候什么时候返回true
    • hashmap里面能不能放null
    • HashMap 的长度为什么是2的幂次方
    • HashMap什么时候会转化为红黑树?
    • 红黑树这么优秀,hashmap为何不直接使用红黑树得了?

HashMap知识点

1、默认初始化容量大小1<<4 = 16;
2、最大容量1<<30 = 2的30次方;
3、默认负载因子0.75f;
4、链表树形化阈值为8(用于hash冲突产生链表还是产生红黑树的一个判断值);TREEIFY_THRESHOLD =8;如果哈希函数不合理,即使扩容也无法减少箱子中链表的长度,因此 Java 的处理方案是当链表太长时,转换成红黑树。这个值表示当某个箱子中,链表长度大于 8 时,有可能(第6点是确定因素)会转化成树;
5、在哈希表扩容时,如果发现链表长度小于 6,则会由树重新退化为链表;UNTREEIFY_THRESHOLD =6
6、在转变成树之前,还会有一次判断,只有键值对数量大于 64 才会发生转换。这是为了避免在哈希表建立初期,多个键值对恰好被放入了同一个链表中而导致不必要的转化;

Java中的HashMap、LinkedMap、TreeMap解释下?

(TreeMap红黑树,有序,HashMap无序,数组+链表/红黑树)

  • HashMap可实现快速存储和检索,但其缺点是其包含的元素是无序的,这导致它在存在大量迭代的情况下表现不佳

  • LinkedHashMap继承自HashMap,相比于HashMap,linkedHashMap结构中还维护着一个双向链表,用于记录顺序。可分为插入顺序和访问顺序两种。如果是访问顺序,那put和get操作已存在的Entry时,都会把Entry移动到双向链表的表尾(其实是先删除再插入)。默认是插入顺序。LinkedHashMap映射减少了HashMap排序中的混乱,且不会导致TreeMap的性能损失,仅仅增加了双向指针。
    LinkedHashMap

  • TreeMap能便捷的实现据元素的 Key 进行各种排序,put时会对key进行比较。TreeMap是基于红黑树构建,其一般性能比前两种map差,因为需要对key排序。
    Java基础:关于hashmap的问题_第1张图片

hashmap为什么线程不安全?

首先,size没有用volatile关键字修饰,这表示其不具有内存可见性,在多线程情况下,对其的修改无法即时被其他线程看到。在多个线程进行put操作时,在resize时可能会出现脏读,从而导致size错误。
另外,在put操作时,HashMap并没有进行任何针对多线程的加锁操作,而我们看ConcurrentHashMap,在putVal中,其在进行插入操作时,用synchronized关键字包裹代码块,来保证其在多线程下的线程安全。
JDK7时resize可能造成链表成环,JDK8已经修复该问题hashmap的resize成环问题
ConcurrentHashMap如何避免死锁?

hashmap怎么解决hash冲突

hashmap解决hash冲突

  • 首先,hashmap在hash冲突时会将其追加到链表或红黑树中。
  • 另外,hashmap会通过再hash来尽可能避免hash冲突,再hash是通过(h >>> 16)把hash值(32位)的高16位移到低16位的位置,然后和原hash值进行异或(^)
    这样做的好处是,由于hash值范围太大,因此只会取低16位来作为数组下标,但直接取hash值的低16位容易发生hash冲突,所以进行再hash,通过把hash值高16位移到低16位,然后和原hash值进行异或,可以把高位也考虑进来

例如,原本两个hash值的低16位是相同的,而高16位不相同,也会被认为是hash冲突,这样进行异或后,由于高16位不同,异或后的低16位也就不相同了,这样能一定程度上避免hash冲突。

注意:取低位hash作为坐标是通过掩码来实现的,具体取多少位要看数组长度,因为掩码是n-1。例如默认n=16,这掩码15化为二进制是4位,也就是只取hash值低4位作为数组坐标。

两个hashmap相比较的时候什么时候返回true

如果是指比较两个hashmap中的node,那么当node对象相等(即==) 时,或者 key跟value都相等时就返回true。

hashmap里面能不能放null

hashtable不可以,而hashmap是可以的。只能有一个key为null,后来的会覆盖之前的,并且value也可以是null。源码中并没有对key和value做非空判断。同时,对key进行hash时,会判断key是否为null,为null则会返回0.
而hashtable在put()时,首先就会判断value不能为空,否则抛出异常。另外,在对key进行hash时并没有对null做特殊处理。
对 Null key 和 Null value 的支持: HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。但是在 Hashtable 中 put 进的键值只要有一个 null,直接抛出 NullPointerException。

HashMap 的长度为什么是2的幂次方

为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上面也讲到了过了,Hash 值的范围值-2147483648到2147483647,前后加起来大概40亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个40亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。**用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。**这个数组下标的计算方法是“ (n - 1) & hash”。(n代表数组长度)。这也就解释了 HashMap 的长度为什么是2的幂次方。

这个算法应该如何设计呢?

我们首先可能会想到采用%取余的操作来实现。但是,重点来了:“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。” 并且 采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。

HashMap什么时候会转化为红黑树?

  • 链表长度达到8,且数组长度达到64的时候
    链表的平均查找长度是O(n/2),而红黑树是O(logn)。至于为什么是8,据作者说是通过概率算出来的。
  • 如果链表长度达到8,而数组长度小于64时,就会对数组进行扩容,而不会直接把链表转化为红黑树,扩容会新建数组,然后重新取掩码从hash中取坐标,这样可以减少链表长度。

hashmap源码解析

红黑树这么优秀,hashmap为何不直接使用红黑树得了?

说一下自己对于这个问题的看法:我们知道红黑树属于(自)平衡二叉树,但是为了保持“平衡”是需要付出代价的,红黑树在插入新数据后可能需要通过左旋,右旋、变色这些操作来保持平衡,这费事啊。你说说我们引入红黑树就是为了查找数据快,如果链表长度很短的话,根本不需要引入红黑树的,你引入之后还要付出代价维持它的平衡。但是链表过长就不一样了。至于为什么选 8 这个值呢?通过概率统计所得,这个值是综合查询成本和新增元素成本得出的最好的一个值。

你可能感兴趣的:(Java)