HashMap之源码分析(一)持续补充中~

66 阅读4分钟

HashMap是Java中常用的数据结构之一,其底层是通过数组和链表或红黑树来实现的。在深入了解HashMap实现的过程中,需要涉及以下知识点:哈希值、位运算、数组+链表、红黑树、容量和负载因子、并发访问。

一、哈希值

下面开始学哈希值,在学习之前,我们先了解一下这个哈希值常见的用处:

1、数据存储和检索:哈希表可以用来快速地存储和检索数据。哈希表使用哈希算法将数据映射到一个位置,这样就可以通过哈希值快速定位数据的位置。如(Map),数据存储在哪个桶中。

2、加密和解密:哈希算法可以用来加密数据,也可以用来验证数据的完整性。例如,在发送数据时,可以使用哈希算法将数据加密,在接收端使用相同的哈希算法解密。

3、校验文件完整性:哈希算法可以用来生成文件的哈希值,然后将这个哈希值与文件的真实哈希值进行比较,以验证文件的完整性。

4、在密码学中使用:哈希算法在密码学中也有广泛应用,例如用来生成密码的哈希值,或者在数字签名中使用。

挠头.jpg

在Java中的HashMap实现中,调用put方法时需要为key计算一个哈希值,这个哈希值是根据key的每个字符计算得出的。

//map.put()方法
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

static final int hash(Object key) {
    int h;
    //这行代码中包含了三个操作符:条件运算符(`? :`)、位异或运算符(`^`)和无符号右移位运算符(`>>>`)。
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

看到这里可能有同学对这个位运算不太熟悉,因为这东西在项目中也不常用,我这里简单举例一下,熟悉的同学可以跳过这段

// 1.  按位与(&):按位比较两个整数的对应位,并在相应位置上都为1时返回1,否则返回0。
int a = 5; // 二进制表示为101
int b = 3; // 二进制表示为011
int c = a & b; // 二进制表示为001,即1
System.out.println(c); // 输出1

// 2.  按位或(|):按位比较两个整数的对应位,并在相应位置上只要有一个为1就返回1,否则返回0。
int a = 5; // 二进制表示为101
int b = 3; // 二进制表示为011
int c = a | b; // 二进制表示为111,即7
System.out.println(c); // 输出7

 // 3.  按位异或(^):按位比较两个整数的对应位,并在相应位置上只有一个为1时返回1,否则返回0。
int a = 5; // 二进制表示为101
int b = 3; // 二进制表示为011
int c = a ^ b; // 二进制表示为110,即6
System.out.println(c); // 输出6

// 4.  按位取反(~):对一个整数进行按位取反,即将其每一位上的0变为1,1变为0。
int a = 5; // 二进制表示为101
int b = ~a; // 二进制表示为11111111111111111111111111111010,即-6
System.out.println(b); // 输出-6

// 5.  左移(<<):将一个整数的二进制表示向左移动一定的位数,低位补0,高位丢弃。
int a = 5; // 二进制表示为101
int b = a << 2; // 二进制表示为10100,即20
System.out.println(b); // 输出20

// 6.  右移(>>):将一个整数的二进制表示向右移动一定的位数,高位用符号位填充,正数高位补0,负数高位补1。
int a = -5; // 二进制表示为11111111111111111111111111111011,即-5的补码
int b = a >> 2; // 二进制表示为11111111111111111111111111111110,即-2的补码
System.out.println(b); // 输出-2

// 7.  无符号右移(>>>):将一个整数的二进制表示向右移动一定的位数,高位用0填充。
int a = -5; // 二进制表示为11111111111111111111111111111011,即-5的补码
int b = a >>> 2; // 二进制表示为00111111111111111111111111111110,即1073741822
System.out.println(b); // 输出1073741822

下面该方法首先检查哈希码是否已经计算,如果没有计算则通过对字符串中每个字符的哈希码进行乘以31的操作累加得到该字符串的哈希码,并将该值缓存起来。最后返回哈希码值。在计算之前,需要先将不在ASCII码表中的字符转换成对应的整数。

//计算哈希值
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;
        
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}