看完这篇 HashMap ,和面试官扯皮就没问题了(2)

简介: 看完这篇 HashMap ,和面试官扯皮就没问题了(2)

HashMap 1.7 的底层结构


JDK1.7 中,HashMap 采用位桶 + 链表的实现,即使用链表来处理冲突,同一 hash 值的链表都存储在一个数组中。但是当位于一个桶中的元素较多,即 hash 值相等的元素较多时,通过 key 值依次查找的效率较低。它的数据结构如下


image.png


HashMap 大致结构


HashMap 底层数据结构就是一个 Entry 数组,Entry 是 HashMap 的基本组成单元,每个 Entry 中包含一个 key-value 键值对。


transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;


而每个 Entry 中包含 「hash, key ,value」 属性,它是 HashMap 的一个内部类


static class Entry<K,V> implements Map.Entry<K,V> {
  final K key;
  V value;
  Entry<K,V> next;
  int hash;
  Entry(int h, K k, V v, Entry<K,V> n) {
    value = v;
    next = n;
    key = k;
    hash = h;
  }
  ...
}


所以,HashMap 的整体结构就像下面这样


image.png


HashMap 1.8 的底层结构


与 JDK 1.7 相比,1.8 在底层结构方面做了一些改变,当每个桶中元素大于 8 的时候,会转变为红黑树,目的就是优化查询效率,JDK 1.8 重写了 resize() 方法。


image.png


HashMap 重要属性


「初始容量」


HashMap 的默认初始容量是由 DEFAULT_INITIAL_CAPACITY 属性管理的。


static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;


HashMap 的默认初始容量是 1 << 4 = 16, << 是一个左移操作,它相当于是


image.png


「最大容量」


HashMap 的最大容量是


static final int MAXIMUM_CAPACITY = 1 << 30;


这里是不是有个疑问?int 占用四个字节,按说最大容量应该是左移 31 位,为什么 HashMap 最大容量是左移 30 位呢?因为在数值计算中,最高位也就是最左位的 是代表着符号为,0 -> 正数,1 -> 负数,容量不可能是负数,所以 HashMap 最高位只能移位到 2 ^ 30 次幂。


「默认负载因子」


HashMap 的默认负载因子是


static final float DEFAULT_LOAD_FACTOR = 0.75f;


float 类型所以用 .f 为单位,负载因子是和扩容机制有关,这里大致提一下,后面会细说。扩容机制的原则是当 HashMap 中存储的数量 > HashMap 容量 * 负载因子时,就会把 HashMap 的容量扩大为原来的二倍。


HashMap 的第一次扩容就在 DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR = 12 时进行。


「树化阈值」


HashMap 的树化阈值是


static final int TREEIFY_THRESHOLD = 8;


在进行添加元素时,当一个桶中存储元素的数量 > 8 时,会自动转换为红黑树(JDK1.8 特性)。


「链表阈值」


HashMap 的链表阈值是


static final int UNTREEIFY_THRESHOLD = 6;


在进行删除元素时,如果一个桶中存储元素数量 < 6 后,会自动转换为链表


「扩容临界值」


static final int MIN_TREEIFY_CAPACITY = 64;


这个值表示的是当桶数组容量小于该值时,优先进行扩容,而不是树化



「节点数组」


HashMap 中的节点数组就是 Entry 数组,它代表的就是 HashMap 中 「数组 + 链表」 数据结构中的数组。


transient Node<K,V>[] table;


Node 数组在第一次使用的时候进行初始化操作,在必要的时候进行 resize,resize 后数组的长度扩容为原来的二倍。


「键值对数量」


在 HashMap 中,使用 size 来表示 HashMap 中键值对的数量。


「修改次数」


在 HashMap 中,使用 modCount 来表示修改次数,主要用于做并发修改 HashMap 时的快速失败 - fail-fast 机制。


「扩容阈值」


在 HashMap 中,使用 threshold 表示扩容的阈值,也就是 初始容量 * 负载因子的值。


threshold 涉及到一个扩容的阈值问题,这个问题是由 tableSizeFor 源码解决的。我们先看一下它的源码再来解释


static final int tableSizeFor(int cap) {
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}


代码中涉及一个运算符 |= ,它表示的是按位或,啥意思呢?你一定知道 「a+=b 的意思是 a=a+b」,那么同理:a |= b 就是 a = a | b,也就是双方都转换为二进制,来进行与操作。如下图所示


image.png


我们上面采用了一个比较大的数字进行扩容,由上图可知 2^29 次方的数组经过一系列的或操作后,会算出来结果是 2^30 次方。


所以扩容后的数组长度是原来的 2 倍。


「负载因子」


loadFactor 表示负载因子,它表示的是 HashMap 中的密集程度。



相关文章
|
搜索推荐 Java API
一道Java集合排序题,HashMap排序,面试必备
一道Java集合排序题,HashMap排序,面试必备
|
2月前
|
存储 算法 安全
HashMap常见面试题(超全面):实现原理、扩容机制、链表何时升级为红黑树、死循环
HashMap常见面试题:红黑树、散列表,HashMap实现原理、扩容机制,HashMap的jd1.7与jdk1.8有什么区别,寻址算法、链表何时升级为红黑树、死循环
|
4月前
|
存储 安全 Java
一天十道Java面试题----第二天(HashMap和hashTable的区别--------》sleep、wait、join)
这篇文章是关于Java面试的第二天笔记,涵盖了HashMap与HashTable的区别、ConcurrentHashMap的实现原理、IOC容器的实现方法、字节码的概念和作用、Java类加载器的类型、双亲委派模型、Java异常体系、GC如何判断对象可回收、线程的生命周期及状态,以及sleep、wait、join、yield的区别等十道面试题。
一天十道Java面试题----第二天(HashMap和hashTable的区别--------》sleep、wait、join)
|
4月前
|
安全 Java
【Java集合类面试十五】、说一说HashMap和HashTable的区别
HashMap和Hashtable的主要区别在于Hashtable是线程安全的,不允许null键和值,而HashMap是非线程安全的,允许null键和值。
|
7月前
|
存储 算法 Java
如果面试也能这样说HashMap,那么就不会有那么多遗憾!(中)
如果面试也能这样说HashMap,那么就不会有那么多遗憾!
48 0
|
6月前
|
存储 安全 Java
《ArrayList & HashMap 源码类基础面试题》面试官们最喜欢问的ArrayList & HashMap源码类初级问,你都会了?
《ArrayList & HashMap 源码类基础面试题》面试官们最喜欢问的ArrayList & HashMap源码类初级问,你都会了?
41 0
|
7月前
|
Python
2024年Python最新刷爆全网的动态条形图,原来5行Python代码就能实现!,2024年最新Python面试必问的HashMap
2024年Python最新刷爆全网的动态条形图,原来5行Python代码就能实现!,2024年最新Python面试必问的HashMap
2024年Python最新刷爆全网的动态条形图,原来5行Python代码就能实现!,2024年最新Python面试必问的HashMap
|
7月前
|
存储 算法 Java
耗时3天写完的HashMap万字解析,争取一篇文章讲透它,面试官看了都直点头!
耗时3天写完的HashMap万字解析,争取一篇文章讲透它,面试官看了都直点头!
95 3
|
7月前
|
Java API
面试官上来就让手撕HashMap的7种遍历方式,当场愣住,最后只写出了3种
面试官上来就让手撕HashMap的7种遍历方式,当场愣住,最后只写出了3种
44 1
|
设计模式 算法 Java
面试官:JDK1.8 HashMap扩容rehash算法是如何优化的?
本文跟大家聊一聊一个常见的面试题,那就是JDK1.8 HashMap扩容rehash算法是如何优化的?