HashMap是Java中常用的数据结构之一,其底层是通过数组和链表或红黑树来实现的。在深入了解HashMap实现的过程中,需要涉及以下知识点:哈希值、位运算、数组+链表、红黑树、容量和负载因子、并发访问。
一、哈希值
下面开始学哈希值,在学习之前,我们先了解一下这个哈希值常见的用处:
1、数据存储和检索:哈希表可以用来快速地存储和检索数据。哈希表使用哈希算法将数据映射到一个位置,这样就可以通过哈希值快速定位数据的位置。如(Map),数据存储在哪个桶中。
2、加密和解密:哈希算法可以用来加密数据,也可以用来验证数据的完整性。例如,在发送数据时,可以使用哈希算法将数据加密,在接收端使用相同的哈希算法解密。
3、校验文件完整性:哈希算法可以用来生成文件的哈希值,然后将这个哈希值与文件的真实哈希值进行比较,以验证文件的完整性。
4、在密码学中使用:哈希算法在密码学中也有广泛应用,例如用来生成密码的哈希值,或者在数字签名中使用。
在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;
}