首先,我们知道 HashMap 的底层实现是开放地址法 + 链地址法的方式来实现。

即数组 + 链表的实现方式,通过计算哈希值,找到数组对应的位置,如果已存在元素,就加到这个位置的链表上。在 Java 8 之后,链表过长还会转化为红黑树。

这个数组并不是一开始就很大,而是随着 HashMap 里面的值变多,达到 LoadFactor 的界限之后,就会扩容。刚开始的数组很小,默认只有 16。

这个数组大小一定是 2 的 n 次方,因为找到数组对应的位置需要通过取余计算,取余计算是一个很耗费性能的计算,而对 2 的 n 次方取余就是对 2 的 n 次方减一取与运算。所以保持数组大小为 2 的 n 次方,这样就可以保证计算位置高效。

那么这个哈希值究竟是怎么计算的呢?假设就是用 Key 的哈希值直接计算。假设有如下两个 key,哈希值分别是:

key1:

0000 0000 0010 1111 1001 0000 0110 1101

key2:

0000 0000 0010 0000 1001 0000 0110 1101

如果直接使用数组默认大小,取余之后 key1 与 key2 就会到数组同一个下标。其实 key1 和 key2 的高位是不一样的。

由于数组是从小到达扩容的,为了优化高位被忽略这个问题,HashMap 源码中对于计算哈希值做了优化,采用高位16位组成的数字与源哈希值取异或而生成的哈希值作为用来计算 HashMap 的数组位置的哈希值

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

为什么要用异或?首先,对于一个数字,转换成二进制之后,其中为的 1 的位置代表这个数字的特性.对于异或运算,如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。0与0异或是0,0与1异或是1,这样相当于让高位的特性在低位得以体现,所以采用这种算法,减少碰撞。

微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer:

2021-2-17:Java HashMap 的中 key 的哈希值是如何计算的,为何这么计算?的更多相关文章

  1. 【转】HashMap集合中key只能为引用数据类型,不能为基本类型

    在HashMap中,为什么不能使用基本数据类型作为key? 其实和HashMap底层的存储原理有关,HashMap存储数据的特点是:无序.无索引.不能存储重复元素. 存储元素采用的是hash表存储数据 ...

  2. Java - HashMap分别按Key和Value进行排序

    我们都知道,Java中的Map结构是key->value键值对存储的,而且根据Map的特性,同一个Map中 不存在两个Key相同的元素,而value不存在这个限制.换句话说,在同一个Map中Ke ...

  3. Java中String的哈希值计算

    下面都是从String类的源码中粘贴出来的 private int hash; // Default to 0 public int hashCode() { int h = hash; if (h ...

  4. 关于Java读取mysql中date类型字段默认值'0000-00-00'的问题

    今天在做项目过程中,查询一个表中数据时总碰到这个问题:      java.sql.SQLException:Value '0000-00-00' can not be represented as ...

  5. 为什么阿里巴巴Java开发手册中强制要求接口返回值不允许使用枚举?

    在阅读<阿里巴巴Java开发手册>时,发现有一条关于二方库依赖中接口返回值不允许使用枚举类型的规约,具体内容如下: 在谈论为什么之前先来科普下什么是二方库,二方库也称作二方包,一般指公司内 ...

  6. Java异常处理场景中不同位置的返回值详细解析

    Java 异常处理中的返回值在不同位置不同场景下是有一些差别的,这里需要格外注意 具体分以下两种场景: 1 finally语句块没有return语句,即当代码执行到try或者catch语句块中的ret ...

  7. JAVA获取oracle中sequences的最后一个值

    项目中,用到一个序列作单号,框架用的是ssh,在dao层去拿的时候,运行时报错为dual is not mapped,[select *.nextval nextvalue from dual] 后来 ...

  8. [翻译]Java HashMap工作原理

    大部分Java开发者都在使用Map,特别是HashMap.HashMap是一种简单但强大的方式去存储和获取数据.但有多少开发者知道HashMap内部如何工作呢?几天前,我阅读了java.util.Ha ...

  9. 【转】Java HashMap工作原理(好文章)

    大部分Java开发者都在使用Map,特别是HashMap.HashMap是一种简单但强大的方式去存储和获取数据.但有多少开发者知道HashMap内部如何工作呢?几天前,我阅读了java.util.Ha ...

随机推荐

  1. 权限管理3-整合Spring Security

    一.Spring Security介绍 1.框架介绍 Spring 是一个非常流行和成功的 Java 应用开发框架.Spring Security 基于 Spring 框架,提供了一套 Web 应用安 ...

  2. JavaScript中函数的调用!

    JavaScript中函数的调用! 1 普通函数 // 1 普通函数 function fn() { console.log(123); } // 函数名 + 一个小括号! 或者 函数名.call() ...

  3. jQuery 勾选启用输入框

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  4. 宝塔Linux命令

    安装宝塔 Centos安装脚本 5.7:yum install -y wget && wget -O install.sh http://download.bt.cn/install/ ...

  5. leaving sockets open which can trigger a ResourceWarning in some # cases, and look like a memory leak in others

    # -*- coding: utf-8 -*- """ requests.api ~~~~~~~~~~~~ This module implements the Requ ...

  6. 变量隐藏Accidental Variable Shadowing

    6.5 - Variable shadowing (name hiding) | Learn C++ https://www.learncpp.com/cpp-tutorial/variable-sh ...

  7. WPF combobox设置默认选项不生效的问题

    combobox 是常用的控件,当我们需要绑定设置默认选项时,往往会绑定 SelectedItem 属性去设置, 可是你会惊奇地发现SelectedItem的值绑定了, 它依旧是熟悉的模样 根据官方的 ...

  8. DNS欺骗&嗅探监听

    承接上一章,除了arp欺骗之外对的欺骗方法 a)使用kali,对dns文件进行编辑 使用的欺骗工具是ettercap.对dns文件编辑: 添加一个A记录,解释到kali自身: b)进行欺骗 通过命令行 ...

  9. Hive 使用总结

    1 带分区列的表更改列类型 常见的一个场景是Hive里面一个带分区的表,原来是int类型的字段,后来发现数据超过了int的最大值,要改成bigint.或者是bigint要改string或decimal ...

  10. P1046 陶陶摘苹果 Python实现

    题目描述 陶陶家的院子里有一棵苹果树,每到秋天树上就会结出1010个苹果.苹果成熟的时候,陶陶就会跑去摘苹果.陶陶有个3030厘米高的板凳,当她不能直接用手摘到苹果的时候,就会踩到板凳上再试试. 现在 ...