Java集合面试题:HashMap源码分析
文章目录
- 一、HashMap源码
- 二、HashMap数据结构模型图
- 三、HashMap中如何确定元素位置
- 四、关于equals与hashCode函数的重写
- 五、阅读源码
- 基本属性
参考文章:史上最详细的 JDK 1.8 HashMap 源码解析
参考文章:Hash详解
参考文章:hashCode源码分析
参考文章:hashCode方法
参考文章:Java 细品 重写equals方法 和 hashcode 方法
参考文章:native
参考文章:HashMap中确定数组位置为什么要用hash进行扰动
一、HashMap源码
JDK官方文档源码,方便后面对照学习
二、HashMap数据结构模型图
JDK1.8对HashMap进行了比较大的优化,底层实现由之前的“数组+链表”改为“数组+链表+红黑树”。JDK1.8的HashMap的数据结构如下图所示,当链表结点较少时仍然是以链表存在,当链表结点较多时(大于8)会转为红黑树。
三、HashMap中如何确定元素位置
- 通过key.hashcode(),根据key获得hashcode值
- 通过扰动函数hash(),根据hashcode获得hash值
- 通过**(n-1)&hash**判断当前元素存放的位置,这里的n指的是数组的长度
- 上面三个“通过”才可以获取真正意义上hashCode,即table的位置。
- 如果当前位置存在元素的话,equals() 就判断该元素与要存入的元素的hash值以及key是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突
其中jdk1.8中扰动函数hash的源码:
static final int hash(Object key) {int h;// key.hashCode():返回散列值也就是hashcode// ^ :按位异或// >>>:无符号右移,忽略符号位,空位都以0补齐return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
其中看到在获得hash值时将key的hashCode异或上其无符号右移16位,Hashmap这么做原因:
防止一些实现比较差的 hashCode() 方法,使用扰动函数之后可以减少碰撞,进一步降低hash冲突的几率。
四、关于equals与hashCode函数的重写
首先我们必须知道,如果我们要使用HashMap来添加以对象作为key的键值对,那么就必须重写equals和hashCode函数。具体看 “三、HashMap中如何确定元素位置。”
我们先来看一下,没有重写的hashCode和equals的源码,不用怀疑,这就是全部源码。
//返回对象在JVM中的32位内存地址,native解释请看参看文章
public native int hashCode();
//比较两个对象的32位内存地址
public boolean equals(Object obj) { return (this == obj); }
为什么我们需要重写hashCode和equals呢?不重写会怎样呢?看看下面示例
public class Student { private String name;private Integer age;
}/*我现在用同一个人(John,21),创建两个对象student1和student2。由于这两个对象是同一个人,所以这两个对象是相等的,但是结果却是false。
*/
public static void main(String[] args) {Student student1 = new Student();student1.setName("John");student1.setAge("21");Student student2 = new Student();student2.setName("John");student2.setAge("21");System.out.println(student1.equals(student2)); //falseSystem.out.println(student1.hashCode() == student2.hashCode()); //false
}
明明是同一个人(John,22),为什么两个对象就不相等呢?因为我们认为的两个对象相等是根据属性的值来判断的,例如student1和student2都是name=John、age=21,那么我就认为他们是相等的。但是我们再来看看Object的hashCode和equals源码,它才不管student1和student2的属性值是否相等,只要两个对象内存地址不一样就是不相等。
这时候我们就知道要重写hashCode和equals,那么我们重写这两个方法的原则是什么?
- hashCode重写:要根据类的属性值来生成hashCode
- equals重写:要根据类的属性值来进行判断
@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;boolean nameCheck=false;boolean ageCheck=false;if (this.name == student.name) {nameCheck = true;} else if (this.name != null && this.name.equals(student.name)) {nameCheck = true;}if (this.age == student.age) {ageCheck = true;} else if (this.age != null && this.age.equals(student.age)) {ageCheck = true;}if (nameCheck && ageCheck){return true;}return false;
@Overridepublic int hashCode() {int result = 17;result = 31 * result + name.hashCode();result = 31 * result + age;return result;}
现在再来测试一下
public static void main(String[] args) {Student student1 = new Student();student1.setName("John");student1.setAge("21");Student student2 = new Student();student2.setName("John");student2.setAge("21");System.out.println(student1.equals(student2)); //trueSystem.out.println(student1.hashCode() == student2.hashCode()); //true
}
重写equals和hashCode,是指我们在开发项目的时候,要根据自己创建对象来重写。例如我创建了Student类里面有name,id,那么我在重写equals和hashCode时候就要有对name和id的比较判断,如果我创建了Animal类里面有age,owner,那么我在重写equals和hashCode时候就要有对age和owner的比较判断
其实在java 7 有在Objects里面新增了我们需要重新的这两个方法,所以我们重写equals和hashCode还可以使用java自带的Objects,如:
@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Pig pig = (Pig) o;return Objects.equals(name, pig.name) &&Objects.equals(age, pig.age);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
那么如果还是觉得有点麻烦呢? 那就使用lombok的注解,让它帮我们写,我们自己就写个注解!
@EqualsAndHashCode(exclude = {"Age"}) //设置重写不包含的字段
public class Student{private String name;private Integer age;
}
五、阅读源码
HashMap在重点其实就是第三、四点,这里对源码再解读一下。
基本属性
// 默认容量16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 最大容量
static final int MAXIMUM_CAPACITY = 1 << 30; // 默认负载因子0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f; // 链表节点转换红黑树节点的阈值, 当链表结点大于8时,转成红黑树结点
static final int TREEIFY_THRESHOLD = 8; // 红黑树节点转换链表节点的阈值, 当红黑树结点小于7时,转成链表结点
static final int UNTREEIFY_THRESHOLD = 6; // 转红黑树时, table的最小长度
static final int MIN_TREEIFY_CAPACITY = 64; // 链表节点, 继承自Entry
static class Node<K,V> implements Map.Entry<K,V> { final int hash;final K key;V value;Node<K,V> next;// ... ...
}// 红黑树节点
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {TreeNode<K,V> parent; // red-black tree linksTreeNode<K,V> left;TreeNode<K,V> right;TreeNode<K,V> prev; // needed to unlink next upon deletionboolean red;// ...
}
相关文章:

Java集合面试题:HashMap源码分析
文章目录一、HashMap源码二、HashMap数据结构模型图三、HashMap中如何确定元素位置四、关于equals与hashCode函数的重写五、阅读源码基本属性参考文章:史上最详细的 JDK 1.8 HashMap 源码解析参考文章:Hash详解参考文章:hashCode源码分析参考…...
华为OD机试 - 数组合并(Python),真题含思路
数组合并 题目 现在有多组整数数组, 需要将他们合并成一个新的数组。 合并规则, 从每个数组里按顺序取出固定长度的内容合并到新的数组中, 取完的内容会删除掉, 如果该行不足固定长度或者已经为空, 则直接取出剩余部分的内容放到新的数组中, 继续下一行。 如样例 1, 获得长度…...

Vue2创建移动端项目
一、Vscode Vscode 下载安装以及常用的插件 1、Vscode 下载 下载地址:Vscode 中文语言插件 搜索 chinese 主题 Atom 主题 文件图标主题 搜索 icon 源代码管理插件GitLens 搜索 GitLens Live Server _本地服务器 搜索 Live Server Prettier - Code formatt…...
PorterDuffXfermode与圆角图片
版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl 圆角图片 在项目开发中,我们常用到这样的功能:显示圆角图片。 这个是咋做的呢?我们来瞅瞅其中一种实现方式 /*** param bitmap 原图* p…...

如何准备大学生电子设计竞赛
大学生电子设计竞赛难度中上,一般有好几个类型题目可以选择,参赛者可以根据自己团队的能力、优势去选择合适自己的题目,灵活自主空间较大。参赛的同学们可以在暑假好好学习相关内容,把往年的题目拿来练练手。这个比赛含金量还是有…...

【Java容器(jdk17)】ArrayList深入源码,就是这么简单
ArrayList深入源码一、ArrayList源码解析1. MIXIN 的混入2. 属性说明3. 构造方法4. 其他方法(核心)iterator 和 listIterator 方法add方法remove 方法sort方法其他二、ArrayList 为什么是线程不安全的?体现哪些方面呢?三、ArrayLi…...

【Java 面试合集】简述下Java的三个特性 以及项目中的应用
简述下Java的特征 以及项目中的应用 1. 概述 上述截图中就是Java的三大特性,以及特性的实现方案。接下来就每个点展开来说说 2. 封装 满足:隐藏实现细节,公开使用方法 的都可以理解为是封装 而实现封装的有利手段就是权限修饰符了。可以根据…...

git基本概念图示【学习】
基本概念工作区(Working Directory)就是你在电脑里能看到的目录,比如名字为 gafish.github.com 的文件夹就是一个工作区本地版本库(Local Repository)工作区有一个隐藏目录 .git,这个不算工作区,…...

微前端qiankun架构 (基于vue2实现)使用教程
工具使用版本 node --> 16vue/cli --> 5 创建文件 创建文件夹qiankun-test。 使用vue脚手架创建主应用main和子应用dev 主应用 安装 qiankun: yarn add qiankun 或者 npm i qiankun -S 使用qiankun: 在 utils 内创建 微应用文件夹 microApp,在该文件夹…...

记录robosense RS-LIDAR-16使用过程3
一、wireshark抓包保存pcap文件并解析ubuntu18安装wireshark,参考下面csdn教程,官网教程我看的一脸蒙(可能英语太差)https://blog.csdn.net/weixin_46048542/article/details/121730448?spm1001.2101.3001.6650.2&utm_medium…...
【博学谷学习记录】大数据课程-学习第七周总结
Hadoop配置文件修改 Hadoop安装主要就是配置文件的修改,一般在主节点进行修改,完毕后scp下发给其他各个从节点机器 文件中设置的是Hadoop运行时需要的环境变量。JAVA_HOME是必须设置的,即使我们当前的系统中设置了JAVA_HOME,它也…...

154、【动态规划】leetcode ——494. 目标和:回溯法+动态规划(C++版本)
题目描述 原题链接:494. 目标和 解题思路 (1)回溯法 本题的特点是nums中每个元素只能使用一次,分别试探加上nums[index]和减去nums[index],然后递归的遍历下一个元素index 1。 class Solution { public:int res …...
MySQL-窗口函数
窗口函数概念常用窗口函数聚合窗口函数专用窗口函数语法OVER子句window_specwindow_name (命名窗口)partition_clause 分区order_clause 排序frame_clause 范围 (指定窗口大小)使用限制练习准备概念 窗口函数对一组查询执行类似于聚合的操作。然而&#…...

【C++设计模式】学习笔记(1):面向对象设计原则
目录 简介面向对象设计原则(1)依赖倒置原则(DIP)(2)开放封闭原则(OCP)(3)单一职责原则(SRP)(4)Liskov替换原则(LSP)(5)接口隔离原则(ISP)(6)优先使用对象组合,而不是类继承(7)封装变化点(8)针对接口编程,而不是针对实现编程结语简介 Hello! 非常感谢您阅读海…...

[测开篇]设计测试用例的方法如何正确描述Bug
文章目录为什么测试人员要写测试用例?怎样设计测试用例?(总的方面)1.基于需求设计测试用例(总的方面) 2.页面(总的方面) 3.非功能性测试(具体方面) 4.1 等…...
设计模式学习笔记--单例、建造者、适配器、装饰、外观、组合
以下内容根据以下网址及相关视频整理:Android设计模式之单例模式_谬谬清不给我取名字的博客-CSDN博客_android 单例模式 Android设计模式--单例模式的六种实现和单例模式讲解Volatile与Synchronized相关的并发_龙腾腾的博客-CSDN博客_android 单例 volatile java …...
English Learning - Day5 L1考前复习 2023.2.10 周五
English Learning - Day5 L1考前复习 2023.2.10 周五1 单选题:She has the face _________.2 单选题: The goals ________ he fought all his life no longer seemed important to him.3 单选题:Sales director is a position ______ communi…...
C. Prepend and Append
time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Timur initially had a binary string†† s� (possibly of length 00). He performed the following operation several (possibly zero)…...

javassm超市在线配送管理系统
为了解决用户便捷地在网上购物,本文设计和开发了一个超市管理系统。本系统是基于web架构设计,SSM框架 ,使用Mysql数据库管理,综合采用JSP模式来完成系统的相关功能。主要实现了管理员与用户的注册与登陆,个人中心、用户…...
Scratch少儿编程案例-多模式贪吃蛇(无尽和计时)
专栏分享 点击跳转=>Unity3D特效百例点击跳转=>案例项目实战源码点击跳转=>游戏脚本-辅助自动化点击跳转=>Android控件全解手册点击跳转=>Scratch编程案例👉关于作者...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...