HashMap 是重点,几乎是各个大厂必问的知识点。这里你一定要掌握这些知识。
    1、HashMap底层数据结构和优缺点?
    JDK1.7 数组 + 链表、JDK1.8 数组 + 链表 + 红黑树。优点访问时间复杂度 O(1), 很快,缺点不是线程安全的。
    2、hash值计算的算法是什么?就是 key.hashCode() 吗?
    不是,基于 hashCode 进行计算的, 高 16 位和低 16 位进行了异或操作。让高 16 位尽可能计算,减少 hash 冲突的概率。具体:hash=hashCode^hashCode>>>16。一般称之为扰动处理。
    3、默认情况下,put 第一个元素时候容量大小是多少?扩容阈值又是多少?
    put第一个元素的时候默认容量时 16,扩容阈值是 0.75*16=12。
    4、hash寻址如何进行的?
    index = (n-1) & hash等价于 index =hash % n。不过 & 与操作性能更高。
    5、hash值如果计算的相同该怎么解决冲突?
    3种情况,第一种 hash 值相同,key 值也相同,进行覆盖操作。第二种 hash 值相同,key 值不同,链接为单向链表结构,第二种 hash 值相同,key 值不同,单向链表长度达到 8,会将链表变为红黑树。
    6、HashMap扩容后怎么进行 rehash 的?
    对应 hash 冲突产生的 3 中情况,扩容 rehash 也有 3 种情况。
    情况 1: 如果数组位置只有一个值:使用新的容量进行 rehash,即 e.hash & (newCap - 1)
    情况 2: 如果数组位置有链表,根据 e.hash & oldCap == 0 进行判断,结果为 0 的使用原位置,否则使用 index + oldCap 位置, 放入元素形成新链表,这里不会和情况 1 新的容量进行 rehash 与运算了,index+ oldCap 这样更省性能。
    情况 3:如果数组位置有红黑树,根据 split 方法,同样根据 e.hash & oldCap == 0 进行树节点个数统计,如果个数小于 6,将树的结果恢复为普通 Node,否则使用 index + oldCap,调整红黑树位置,这里不会和新的容量进行 rehash 与运算了,index + oldCap 这样更省性能。
    7、指定大小的 HashMap,扩容阈值算法是什么?
    由于指定容量大小时,会计算扩容阈值,会将传入的容量大小调整为最接近 2 的次幂大小,设置为扩容阈值,构造函数时候,只是计算了扩容阈值,没有初始化数组大小。在添加元素时候数组大小等于了扩容阈值。
    为什么这么做呢?一句话,为了提高 hash 寻址和扩容计算的的效率。因为无论扩容计算还是寻址计算,都是二进制的位运算,效率很快。另外之前你还记得取余 (%) 操作中如果除数是 2 的幂次方则等同于与其除数减一的与 (&) 操作。即 hash%size = hash & (size-1)。这个前提条件是除数是 2 的幂次方。
    8、HashMap死循环问题
    在 JDK1.8 引入红黑树之前, JDK1.7 由于只有单向链表解决 hash 冲突,除了遍历性能可能会慢,还有几率在多线程同时扩容,rehash 的时候发生死循环问题,造成 cpu100%。
    造成死循环的核心脉络有如下几步:
    1、首先原位置得有 hash 冲突,比如链表元素有 4 个。
    2、之后需要有 2 个线程,同时添加元素,均满足扩容条件,进行扩容
    3、一个线程刚刚进行了 rehash 操作,之后另一个线程开始 rehash 操作,会形成环向链表
    4、get 操作时候发生无限死循环,cpu 可能达到 100%
    HashMap 的兄弟姐妹们小结
    1、LinkedHashMap 继承与 HashMap,底层结构基于 HashMap,增加了 2 个 before 和 after 指针,形成双向链表,可以维护插入有序或者访问有序,访问有序可用来做 LRUMap 或者适用需要顺序遍历的场景,但是它仍然不是线程安全的。
    2、TreeMap 可以自定义 Key 值的排序规则,底层数据结构是红黑树。应用场景很灵活,可以用来请求参数排序或者最近一次心跳时间、续约时间排序,来实现心跳或续约过期机制等。
    3、HashTable 和 HashMap 核心区别就是使用 synchronized 保证线程安全,这个和 Vector+ArrayList 很像
    4、HashSet 使用了 HashMap,只不过 add 方法时候的 value 都是 new Object() 而已,结合 map 的特性,同一个 key 值只能存在一个,map 在 put 的时候,会 hash 寻址到数组的同一个位置去,然后覆盖原来的值,所以 Set 是去重的。默认是无序的。
    5、LinkedHashSet 继承了 HashSet, 此时 HashSet 通过使用 LinkedHashMap,是可以进行访问有序的保证
    6、TreeSet 也同理,默认是根据 key 值的 compare 方法来排序的,可以自定义 Comparator,底层使用了 TreeMap,add 元素时,同样是空的 Object,同样去重,但是 TreeSet 访问是可以有序。
    7、LinkedHashSet/TreeSet/HashTable/HashSe 的原理都极其简单,都是基于之前的 HashMap、LinkedHashMap、TreeMap 而已。