HashMap的源码分析 | Java Debug 笔记

147 阅读2分钟

本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看活动链接

一 数据结构

首先就要理解HashMap是什么样的结构,咱们先上图

image.png

这个就是HashMap的拉链式存储的数据结构,使用数组加链表

二 key值的产生

key是怎么产生的,如果你之前稍有了解,但是没看过源码,肯定会想到和hashCode方法有关,但是源码中的处理是

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}

先说说 这个函数是啥?这东西学名叫做扰动函数,再说说为啥这样干,我们看到这个函数中做了一个hashCode处理,但是还没完,他又做了异或和右移16位的操作,那问题来了,为啥还要这么干?

是由于hashCode产生的key范围太大了[-2147483648, 2147483647],有将近40亿的长度。而后续的处理就是为了缩小这个长度,以及让数据更散列。

三 HashMap的初始化大小

HashMap的初始化大小是多少?相信有一部分的小伙伴会说是16,然后还有很的小伙伴不知道,为啥?因为他们根本没接触过扩容。所以就引出了另一个学名初始化容量,初始化容量一定是2的倍数,扩容后也是2的倍数,因为只有2的倍数-1 才会在二进制中出现0001的数值。
下面提供一个计算扩容后函数的源码,学名 计算阈值大小的方法

   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;}
    

扩容还会提到一个学名负载因子,默认的负载因子为0.75,也就是数组塞到3/4就进行扩容。