HashMap源码剖析

1.JDK7版本创建与添加数据的的过程

(1). HashMap<String, Integer> map =new HashMap<>();

//创建对象过程中,底层会初始化数组Entry[] table =new Object[16];16是2的倍数.

...

map.put("hexua", 66);

将"hexua"和66封装到一个Entry对象entry中.并将此对象添加到table数组中.

(2). 添加修改的过程.

将(key1, value1)添加到当前的map中
首先需要调用key1所在类的hashCode()方法,计算key1对象的哈希值1(往往是根据key1中的属性计算的), 此哈希值再经过hash()方法后,得到哈希值2,哈希值2再经过indexFor方法后,确定了(key1, value1)在table数组中的索引位置i;|----> 如果此索引位置上没有元素(table[i] == null), 则(key1, value1)添加成功|---->如果此索引位置上已有元素(key2, value2), 则需要比较key2,key1的哈希值2是否相等.|---->如果二者的哈希值2不相等,则(key1, value1)添加成功|---->如果二者还相等,则继续比较二者的equals();调用key1的equals,key2作为参数传入|---->如果equals返回flase,添加成功|---->返回true,说明key1,key2是相同的,默认情况下,value1替换value2;

说明 :

  • 如果索引i处无元素,(key1, value1)被添加到该索引处.
  • 如果索引i处只有一个元素,则(key1, value1)与(key2, value2)构成单向链表.如果索引i处有一条单向链表,则按照头插法插入到该链表中.

(3). 随着不断插入数据,在满足下列条件的情况下,可以考虑扩容.

(size >=threshoud) && (null !=table[i])

  • threshold为临界值,当添加的元素的个数>threshold(数组的长度*加载因子,加载因子一般被初始化为0.75f)时.考虑扩容.
  • 默认的临界值 : 16*0.75=12.默认扩容为原来的两倍.始终保证数组的长度是2的整数倍.

2.JDK8与JDK7的不同之处

(因为我下载的是JDK17版本,听说17与8版本差别不大,所以在此查看的是17版本的源码.)

(1). 

HashMap<String, Integer> map =new HashMap<>();

//底层并不会初始化数组.只是做了一个初始化操作.

public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
loadFactor是加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
DEFAULT_LOAD_FACTOR全局常量为0.75

当首次put(key1, value1)时,才会初始化数组.

put方法底层会调用到putVal方法.

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}

26f0ff668f63409eabb3d5516f13117a.png

首先key被扔到hash()方法中,我们查看hash方法的源码.

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

先调用key的hashCode()方法计算得到的哈希值1,再进行计算,得到哈希值2.并将哈希值2返回作为putVal方法的实参传入.

我们再看putVal方法的源码.

table是Node类型的数组.Node实现了Map.Entry接口.类似于JDK7中的Entry类.

transient Node<K,V>[] table;static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> next;Node(int hash, K key, V value, Node<K,V> next) {this.hash = hash;this.key = key;this.value = value;this.next = next;}

第一个if判断table是否分配了的内存空间,第一次put(key1, value1),table =null.所以table调用了resize()方法,初始化table数组.

我们再查看resize()源码.

ec9df1955ca2400581f6028d212d45a1.png

oldTable指向了table.而且oldCap等于0.所以并没有进入到前面的if(oldCap >0)等语句中.直接看else语句.

else {        // zero initial threshold signifies using defaultsnewCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final float DEFAULT_LOAD_FACTOR = 0.75f;

可以清晰的看到 :

newCap为初始化时数组的长度.即16.

newThr为初始化时数组的临界值,即12.

9fe4617cbb0849c5b74c3db5d40ef381.png

截取了部分代码.可以看到,底层new了一个长度为16的Node类型的数组.而且将数组返回.

按住Ctrl+Alt+←

再查看putVal源码.

if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);

第二个if语句中,(n-1)&hash求出索引i.如果tab[i]为null意味着此处无元素,那么将新new的Node对象放到该数组位置上.

至于(n-1)&hash设计的很巧妙.由于n为数组的长度.数组的长度都是2的倍数.如初始化时数组的长度为16.n-1用二进制表示为1111.那么只需对比hash二进制表示的后四位进行位运算.计算得到的索引必然也是在0到15内.这也是为什么每次数组扩容默认的都是2倍的原因.

因为执行了上面的if语句,下面的else语句跳过.执行第三个if语句.

8d7febe648604c349e2356575b6e8b2e.png

如果++size>临界值,那么将扩容.

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/615135.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java springmvc 参数名用is开头导致为null

因为最近在整理一些源码和编写规范&#xff0c;这里写一下只是记录几年前自己遇到的问题&#xff0c;好久都忘了&#xff0c;还是写下来比较好。 问题记录&#xff1a;由于变量使用了boolean&#xff0c;并且变量名是is开头的&#xff0c;由于java机制boolean默认是false&#…

变量---

一、变量概述 1、什么是变量 变量是用于存放数据的容器。通过变量名 获取数据&#xff0c;甚至数据可以修改。 变量的本质&#xff1a;变量是程序在内存中申请的一块用来存放数据的空间。 二、变量的使用 变量在使用时分两步&#xff1a;1、声明变量 2、赋值 三、变量语法…

【回溯】Leetcode 51. N 皇后【困难】

N 皇后 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方案。…

2024年第十四届MathorCup数学应用挑战赛B题解题思路

B题https://mbd.pub/o/bread/ZZ6Wm5dx 问题1:对于附件I(Pre_test文件夹)给定的三张甲骨文原始拓片图 片进行图像预处理&#xff0c;提取图像特征&#xff0c;建立甲骨文图像预处理模型&#xff0c;实现对 甲骨文图像干扰元素的初步判别和处理。 针对问题1&#xff0c;对于附件…

vue源码解析——v-if和v-for哪个优先级高,如何避免两者同时使用

首先&#xff0c;官方不推荐v-if和v-for在同一个元素上使用。其次&#xff0c;如果两者同时使用&#xff0c;v-if和v-for的优先级怎么确定&#xff1f;在vue2和vue3中这两者的优先级顺序不一样。vue2是v-for优先&#xff0c;条件不存在时也会渲染多个注释节点。在vue3中进行了改…

Android - 安卓概述

什么是安卓? Android 是一种基于 Linux 的开源操作系统&#xff0c;适用于智能手机和平板电脑等移动设备。 Android 是由 Google 和其他公司领导的 Open Handset Alliance 开发的。 Android 为移动设备的应用程序开发提供了统一的方法&#xff0c;这意味着开发人员只需为 And…

多模块项目使用springboot框架进行业务处理

项目目录 1、在Result定义返回结果 package com.edu.result;import lombok.Data;import java.io.Serializable;/*** 后端统一返回结果* param <T>*/ Data public class Result<T> implements Serializable {private Integer code; //编码&#xff1a;1成功&#xf…

聚酰亚胺PI材料难于粘接,用什么胶水粘接?那么让我们先一步步的从认识它开始(二十五): 聚酰亚胺光敏PI(PSPI)

聚酰亚胺光敏PI&#xff08;PSPI&#xff09; 聚酰亚胺光敏PI&#xff08;PSPI&#xff09;是一种结合了聚酰亚胺&#xff08;PI&#xff09;的优良物理和化学性能以及光敏材料特性的高端有机材料。在高分子链上&#xff0c;它兼有亚胺环和光敏基因&#xff0c;因此具有优异的热…

蓝桥杯 前一晚总结 模板 新手版

《准备实足&#xff0c;冲冲冲 省一》https://www.yuque.com/lenyan-svokd/hi7hp2/hfka297matrtsxy2?singleDoc# 《准备实足&#xff0c;冲冲冲 省一》 #include<bits/stdc.h> // 包含标准库头文件using namespace std; using ll long long; // 定义 long long 数据类…

防火墙操作!

当小编在Linux服务器上部署好程序以后&#xff0c;但是输入URL出现下述情况&#xff0c;原来是防火墙的原因&#xff01;&#xff01; 下面是一些防火墙操作&#xff01; 为保证系统安全&#xff0c;服务器的防火墙不建议关闭&#xff01;&#xff01; 但是&#xff0c;我们可…

免费ssl证书能一直续签吗?如何获取SSL免费证书?

免费SSL证书是否可以一直续签。我们需要了解SSL证书的基本工作原理。当你访问一个使用HTTPS协议的网站时&#xff0c;该网站实际上在使用一个SSL证书。这个证书相当于一个数字身份证明&#xff0c;它验证了网站的真实性和安全性。而这个证明是由受信任的第三方机构——通常是证…

RestTemplate—微服务远程调用—案例解析

简介&#xff1a;总结来说&#xff0c;微服务之间的调用方式有多种&#xff0c;选择哪种方式取决于具体的业务需求、技术栈和架构设计。RESTful API和HTTP客户端是常见的选择&#xff0c;而Feign和Ribbon等辅助库可以简化调用过程。RPC和消息队列适用于特定的场景&#xff0c;如…