zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Java HashMap 核心源码解读

JAVA源码 核心 解读 HashMap
2023-09-27 14:23:39 时间

本篇对HashMap实现的源码进行简单的分析。 所使用的HashMap源码的版本信息如下:

核心源码解读

/*

* @(#)HashMap.java 1.73 07/03/13

* Copyright 2006 Sun Microsystems, Inc. All rights reserved.

* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

*/

一.概述

在Java中每一个对象都有一个哈希码,这个值可以通过hashCode()方法获得。hashCode()的值和对象的equals方法息息相关,是两个对象的值是否相等的依据,所以当我们覆盖一个类的equals方法的时候也必须覆盖hashCode方法。

例如String的hashCode方法为:

public int hashCode() {

int h = hash;

if (h == 0) {

int off = offset;

char val[] = value;

int len = count;

for (int i = 0; i len; i++) {

h = 31*h + val[off++];

hash = h;

return h;

}

可以看得出,一个字符串的哈希值为s[0]31n-1 + s[1]31n-2 + … + s[n-1],是一个整数。也就是说所有的字符串可以通过hashCode()将其映射到整数的区间中,由于在java中整数的个数是有限的(四个字节有正负,第一位为符号位-231 ~ 231 -1),当s[0]31n-1 + s[1]31n-2 + … + s[n-1]足够大的时候可能会溢出,导致其变成负值。从上面的情况我们可以看出两个不同的字符串可能会被映射到同一个整数,发生冲突。因此java的开 发人员选择了31这个乘数因子,尽量使得各个字符串映射的结果在整个java的整数域内均匀分布。

谈完java对象的哈希码,我们来看看今天的主角HashMap,HashMap可以看作是Java实现的哈希表。HashMap中存放的是 key-value对,对应的类型为java.util.HashMap.Entry,所以在HashMap中数据都存放在一个Entry引用类型的数组 table中。这里key是一个对象,为了把对象映射到table中的一个位置,我们可以通过求余法来,所以我们可以使用 [key的hashCode table的长度]来计算位置(当然在实际操作的时候由于需要考虑table上的key的均匀分布可能需要对key的hashCode做一些处理)。

二.源码解析

相关属性 首先肯定是需要一个数组table,作为数据结构的骨干。

transient Entry[] table;

这边定义了一个Entry数组的引用。 继续介绍几个概念把

capacity容量 是指数组table的长度
loadFactor 装载因子,是实际存放量/capacity容量 的一个比值,在代码中这个属性是描述了装载因子的最大值,默认大小为0.75
threshold(阈值)代表hashmap存放内容数量的一个临界点,当存放量大于这个值的时候,就需要将table进行夸张,也就是新建一个两倍大的数组,并将老的元素转移过去。threshold = (int)(capacity * loadFactor);

put方法详解

public V put(K key, V value) {

 if (key == null)

 return putForNullKey(value);

 int hash = hash(key.hashCode());

 int i = indexFor(hash, table.length);

 for (Entry K,V e = table[i]; e != null; e = e.next) {

 Object k;

 if (e.hash == hash ((k = e.key) == key || key.equals(k))) {

 V oldValue = e.value;

 e.value = value;

 e.recordAccess(this);

 return oldValue;

 modCount++;

 addEntry(hash, key, value, i);

 return null;

 }

在HashMap中我们的key可以为null,所以第一步就处理了key为null的情况。
当key为非null的时候,你也许会认为:恩,直接和table长度相除取模吧,但是这里没有,而是又好像做了一次哈希,这是为什么呢?这个还得先看indexFor(hash, table.length)方法,这个方法是决定存放位置的

static int indexFor(int h, int length) {

 return h (length-1);

 }

明眼的都可以发现,因为在HashMap中table的长度为2n (我们把运算都换成二进制进行考虑),所以h (length-1)就等价于h%length,这也就是说,如果对原本的hashCode不做变换的话,其除去低length-1位后的部分不会对 key在table中的位置产生任何影响,这样只要保持低length-1位不变,不管高位如何都会冲突,所以就想办法使得高位对其结果也产生影响,于是 就对hashCode又做了一次哈希

static int hash(int h) {

 // This function ensures that hashCodes that differ only by

 // constant multiples at each bit position have a bounded

 // number of collisions (approximately 8 at default load factor).

 h ^= (h 20) ^ (h 12);

 return h ^ (h 7) ^ (h 

 }

当找到key所对应的位置的时候,对对应位置的Entry的链表进行遍历,如果以及存在key的话,就更新对应的value,并返回老的 value。如果是新的key的话,就将其增加进去。modCount是用来记录hashmap结构变化的次数的,这个在hashmap的fail- fast机制中需要使用(当某一个线程获取了map的游标之后,另一个线程对map做了结构修改的操作,那么原先准备遍历的线程会抛出异常)。 addEntry的方法如下

void addEntry(int hash, K key, V value, int bucketIndex) {

 Entry K,V e = table[bucketIndex];

 table[bucketIndex] = new Entry K,V (hash, key, value, e);

 if (size++ = threshold)

 resize(2 * table.length);

 }

get方法

public V get(Object key) {

 if (key == null)

 return getForNullKey();

 int hash = hash(key.hashCode());

 for (Entry K,V e = table[indexFor(hash, table.length)];

 e != null;

 e = e.next) {

 Object k;

 if (e.hash == hash ((k = e.key) == key || key.equals(k)))

 return e.value;

 return null;

 }

get方法其实就是将key以put时相同的方法算出在table的所在位置,然后对所在位置的链表进行遍历,找到hash值和key都相等的Entry并将value返回。


来源:51CTO


阿里巴巴新产“Java架构核心宝典”,全是流行技术,限时开放 什么是架构师?对于程序员来说,聊架构是一个永不过时的话题。实际上,每一家公司都有自己对架构师不同的定位,因为不同的公司,所处的阶段、业务模式以及应用场景都不一样,因此对架构师的要求不一样,所以定位也就不同。 但是,无论如何,架构师除了优秀的合作能力以及清晰的思路头脑以外,过硬的技术基础也是很有必要的,大型的互联网公司对架构师的技术要求也是非常高的。因此,学习架构技术,刻不容缓。
Java报告推送失败补偿机制;钉钉群通知消息核心代码 Java报告推送失败补偿机制,超过次数后使用钉钉通知开发 自动补偿实现: 要求方法调用的过程中,失败的时候,系统有办法进行自动重试,重试达到一定次数后,钉钉通知开发。 实现设计:注解,反射,定时任务
复习这份美团架构师的Java核心面试宝典,我四面阿里拿下offer 怎样才能拿到大厂的offer,没有掌握绝对的技术,那么就要不断的学习 他是如何拿下阿里等大厂的offer的呢,今天分享他的秘密武器,美团资深架构师整理的Java核心知识点,面试时面试官必问的知识点,篇章包括了很多知识点,其中包括了有基础知识、Java集合、JVM、多线程并发、spring原理、微服务、Netty 与RPC 、Kafka、日记、设计模式、Java算法、数据库、Zookeeper、分布式缓存、数据结构等等。
阿里藏经阁天花板:高性能Java架构核心原理手册,一定要偷偷看 市面上讲Java框架的书很多,包括Sping Boot、Spring Cloud、Kafka等,但这些书通常只会让你技术的“量”增长,而“质”仍处于SSM的阶段。而且互联网上并没有体系化、结构化的提升技术的“质”的教材,于是这份阿里的高性能java架构核心手册就出来了!
GitHub上这份阿里的Java高并发核心手册,即使再过20年依然“NB” 什么是高并发? 高并发(High Concurrency)通常是指通过设计保证系统能够同时并行处理很多请求。 通俗来讲,高并发是指在同一个时间点,有很多用户同时访问同一 API 接口或者 Url 地址。它经常会发生在有大活跃用户量,用户高聚集的业务场景中。 今天给大家分享一份由一位阿里大牛亲自操刀写出来的一份:Java高并发核心编程手册,号称即使再过20年这份资料依然不会被淘汰!