HashMap真面目

背景

今天数据采集项目碰到一个性能问题,3000多个采集点,每一个采集点每秒送一个数据,接收到数据之后首先需要内存中做缓存,之后有一系列的业务分析处理,所以,对系统性能要求比较高。

最近几天发现服务器cpu大多时间都在100%以上,所以就重点分析了缓存方案,确实是缓存方案有问题,修改之后初步验证,cpu降低到50%左右,更换了第二个缓存方案之后,cpu降低到了大概30%左右。

所以,不同的缓存方案,对于高并发场景下应用性能的影响还是蛮大的。

java中的缓存,大部分都是通过HashMap实现的,突然想到之前就记录过HashMap学习笔记,找了半天才找到,差点丢了,重新找回来做个记录。

搞一个Map学习系列,从HashMap开始。

认识HashMap

HashMap是java中应用最广的集合类之一,以key/value(键值对)的方式保存数据。

你可以把HashMap叫做集合类,也可以把它叫做容器,java中许多容器框架比如Spring,其实好多都是用HashMap来存储数据的。

当然,java秉承“一切都是对象”,HashMap中存储的当然也是对象,只不过是以“键值”对组成的对象。

HashMap继承自AbstractMap,并实现了Map接口。所以,想要彻底搞懂HashMap,还是需要先从Map接口、以及AbstractMap入手。

Map接口

其实Map接口没啥东西,接口而已。定义了size()、isEmpty()、get()、put()、containsKey()、containsValue()…等通用的Map类方法。

相对重要一点的是,Map接口定义了一个Entry<K,V>内部接口,这个Entry其实就是Map包含的对象,不同的Map的实现类会有不同的实现。

Map接口也实现了几个方法,具体暂时就不详细分析了,这其实是一个很好的针对“接口是否可以实现方法?”这个问题的很好的答案。

AbstractMap抽象类

AbstractMap抽象类实现了Map接口,具体化了部分Map接口定义的方法。

实现了一个叫SimpleEntry的Entry(就是Map接口中定义的内部接口),还有一个叫SimpleImmutableEntry的Entry。

暂且不表,不影响主题:识别HashMap真面目。

HashMap的数据结构

回过头来再继续研究HashMap,首先识别HashMap的数据结构,我们先从简单的入手,一步一步抽丝剥茧、先易后难,逐步研究。

首先来认识一下Node。
Node是Entry的实现,数据结构非常简单:

 final int hash;final K key;V value;Node<K,V> next;

哈希值、key、vaue以及指向下一节点的简单的链表结构。

HashMap桶内Node链表容量增大之后会自动修改简单链表结构为红黑树,本篇暂不研究红黑树。

table数组
table数组是HashMap真正存储数据的地方,所以说白了HashMap底层实际上还是数组。

不过HashMap的table是比较特殊的数组,数组内的每一个对象其实就是我们常说的,桶内装的是Node<K,V>对象,也就是我们放置到HashMap中的键值对。

HashMap的初始化

HashMap提供了几个不同的初始化方法,区别无非就是有没有初始化容量大小、有没有初始化对象、有没有初始化的loadFactor和threshold。

这几个概念需要我们一个个慢慢去了解。

  1. 容量
    就是HashMap中table数组的大小,HashMap的容量是2的n次方,初始化不设置容量的话,默认16,初始化如果设置了
    initialCapacity 的话,则HashMap的容量是最接近initialCapacity并且大于initialCapacity的2的整数次幂,比如initialCapacity设置为3,4,5则HaspMap最终容量为8,设置为9,10,11…则HashMap的最终容量为16,以此类推。

    这个容量计算是通过tableSizeFor方法实现的,我们暂时按下好奇心(这个方法为什么能实现大于输入参数cap的最近的2的整数次幂?)。

static final int tableSizeFor(int cap) {int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;}
  1. loadFactor
    直译为装载因子,意译为扩容因子,初始值设置为0.75。
  2. threshold
    threshold是扩容阈值,HashMap的容量loadFactor=threshold。当HashMap存放对象的数量增长到threshold的时候进行扩容。
    比如HashMap初始化容量为16,默认loadFactor为0.75,则threshold=16
    0.75=12。则当HashMap存储对象的数量(size)大于12的时候,HaspMap调用resize()方法自动扩容。
    4.size
    可以通过调用size()方法获取到,HashMap内实际存放的对象数。我们如果要想搞清楚HashMap的真面目,最好能对size和容量Capacity有清楚的认识。size是对应用很有价值的数据,我们开发过程中所说的一个HashMap的大小其实指的就是size。而Capacity对应用来说没有什么意义,只是HashMap内部使用的概念,只对那些对HashMap内部结构有兴趣、想要研究清楚其工作机制的同学有意义。

###HashMap赋值
HashMap的赋值逻辑如下(假设待存放的数据为e<key1,value1>):

  1. 检查table数组为空的话,初始化指定容量或者默认容量的table数组
  2. 根据key1的哈希值计算得出(算法为(容量 - 1) & hash(key1))对应的桶。这一步很重要,一般来讲优秀的hash算法能够尽可能确保不同的key值得到不同的hash值,也就可以确保放入不同的桶内。但是不可避免的,可能会存在不同key值得到相同hash值的情况(hash冲突:key1<>key2,hash(key1)=hash(key2)),这种情况下就会放置在相同的桶(比如table[5])内。
  3. 得到桶之后,判断桶内是否已经有数据。
  4. 没有数据则直接new一个Node:newNode(hash, key1, value1, null),放在桶中,结束。
  5. 否则,桶内有数据,有两种情况:一是为键值key1重复赋值、二是hash冲突。
  6. 如果是hash冲突,则new一个Node:newNode(hash, key1, value1, null)并将其设置为桶内的最后一个Node。
  7. 如果是重复赋值(桶内数据的key值=key1),则为key1重新赋值value1,并返回key1的旧值

所以我们可以看到,针对key而言,HashMap不重复,意思是说,相同的key只在HashMap中只保留一份数据。

并且,一般情况下,HashMap的一个桶内只保留一个对象,只有在hash冲突发生了之后,桶内才有可能放置多于一个对象,以链表结构保存。

HashMap中的null对象

此处null对象指的是HashMap中的key值为null的Node对象。

大家都知道HashMap允许且仅能存储key=null的一个对象,比如代码:

    HashMap hm<String,String> = new HashMap();hm.put(null,"it's null");hm.put(null,"i am null");hm.put(null,"null loves null");

最终hm容器中只有一个null对象,并且hm.get(null)得到的应该是 “null loaves null”。

这背后的原因可以从上述HashMap赋值逻辑中找到答案,你只要知道null的hash值是0就可轻易得出以上结论。

从HashMap获取数据

通过get(key)方法获取数据的逻辑如下(假设要获取的数据key=key1):

  1. table数组不为空并且数组长度大于0,则采用与put数据相同的算法得到key1值对应的桶。
  2. 桶内不空则从第一个节点开始检查,如果节点key值等于key1,则返回该节点的value。如果第一个节点不满足条件,则依次检查桶内所有其他节点。
  3. 桶内空,或者桶内不空但是没有找到满足条件的对象(应该不可能)则返回null,表明当前HashMap中不存在key值为key1的对象

需要注意的一点是,检查节点key值等于key1的逻辑是:
两个对象相等,或者两个对象不为null且key1.equals(key)。

好了,以上!相信已经能够揭开HashMap神秘面纱之一角了。

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

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

相关文章

STM32CubeMX-H7-19-ESP8266通信(中)--单片机控制ESP8266实现TCP地址通信

前言 上篇文章我们已经能够使用串口助手实现esp8266的几种通信&#xff0c;接下来我们使用单片机控制实现。这篇文章会附带教程&#xff0c;增加.c和,.h&#xff0c;把串口和定时器放到对应的编号&#xff0c;然后调用初始化就可以使用了。 先讲解&#xff0c;然后末尾再放源码…

欧盟RED网络安全标准EN 18031-2的要求

欧盟RED网络安全标准EN 18031-2的要求 欧盟RED网络安全标准EN 18031-2的要求 ​ 适用产品范围&#xff1a; 能够处理个人隐私数据的可联网无线电设备。 不具备联网能力的三类无线电设备&#xff1a;玩具、儿童护理类设备、可穿戴类设备。 主要测试与评估内容&#xff1a; EN…

一起了解--CAST函数

CAST函数在SQL中用途广泛&#xff0c;不仅可以转换为数值类型&#xff0c;还可以在多种场景下用于数据类型转换。以下是一些常见的用途和示例&#xff1a; 类型转换 使用CAST函数可以在查询数据库时根据需要调整数据格式或类型 CAST(expression AS target_type) expression …

(50)课71:查看指定 query_id 的 SQL 语句的执行各个阶段的耗时情况 show profile for query query_id;

&#xff08;137&#xff09;查看指定 query_id 的 SQL 语句的执行各个阶段的耗时情况 show profile for query query_id &#xff1a; &#xff08;138&#xff09; 谢谢

AWS中国云的定时任务(AWS EventBridge+AWS Lambda)

问题 最近有一个每天在凌程定时同步数据给第三方系统的需求。需要使用AWS EventBridge和AWS Lambda结合的方式来同步数据给第三方系统。 思路 使用Python的ORM框架(例如&#xff1a;SQLAlchemy)查询到需要同步的数据&#xff0c;然后&#xff0c;使用http客户端&#xff08;…

开源PSS解析器

本章介绍开源PSS解析工具&#xff1a; 1. PSSTools语法解析器&#xff0c;这个工具仅包含一个语法解析器。 2. gen-pss&#xff0c;实现了语法解析器&#xff0c;和简单的Test realization&#xff0c;没有约束求解器。 本文将改造并使用gen-pss来生成C测试用例&#xff0…

《linux2.4 内存管理》:第 2 章 描述物理内存

Linux 适用于多种体系结构&#xff0c;需用体系结构无关方式描述内存。本章介绍影响 VM 行为的内存簇、页面和标志位结构。 非一致内存访问&#xff08;NUMA&#xff09;&#xff1a;在 VM 中&#xff0c;大型机器内存分簇&#xff0c;依簇与处理器距离&#xff0c;访问代价不…

数据湖是什么?数据湖和数据仓库的区别是什么?

目录 一、数据湖是什么 &#xff08;一&#xff09;数据湖的定义 &#xff08;二&#xff09;数据湖的特点 二、数据仓库是什么 &#xff08;一&#xff09;数据仓库的定义 &#xff08;二&#xff09;数据仓库的特点 三、数据湖和数据仓库的区别 &#xff08;一&#…

Smart Form Adobe form

强制更改内表:TNAPR se16-> Smart Form总览 Smart form 变量格式说明: &symbol& (括号中,小写字母为变量) &symbol& 屏蔽从第一位开始的N位 &symbol (n)& 只显示前N位 &symbol (S)& 忽略正负号 &symbol (<)& 符号在…

Linux 内核学习(11) --- Linux 链表结构

文章目录 Linked List 简介Linked List 操作方法链表头结点初始化创建链表节点添加节点到链表中从链表中删除节点从链表中替换节点移动链表中的节点检查链表链表遍历demo 实例 Linked List 简介 链表是一种数据结构&#xff0c;由一系列节点组成&#xff0c;每个节点包含数据部…

一分钟部署nginx-公网IP访问内网

前言 服务器内网下有nacos cluster&#xff08;3个节点&#xff09;&#xff0c;开放到公网并指定公司网络访问需要配置三次IP白名单&#xff0c;因此需要简化流程&#xff0c;通过nginx反向代理只配置1次IP白名单。 现在通过docker容器模拟环境&#xff0c;准备1台云服务器。…

C 语言分支与循环

目录 一. 分支结构&#xff1a;if 语句与 switch 语句 1. if 语句 2. switch 语句 二、关系操作符、条件操作符与逻辑操作符 1. 关系操作符 2. 条件操作符 3. 逻辑操作符 三、循环结构&#xff1a;while 循环、for 循环与 do - while 循环 1. while 循环 2. for 循…

【一文看懂Spring Boot2.x升级Spring Boot3.x】springboot2.x升级springboot3.x

springboot2.x升级springboot3.x 背景升级jdk版本为17以上springboot版本修改javax包更新mybatis-plus升级swagger升级springdocspringdoc配置背景 当前项目是springboot2.5.9版本的springboot+mybatis-plus项目,需要升级到springboot3.5.0项目。 升级jdk版本为17以上 Spri…

阳台光伏防逆流电表革新者:安科瑞ADL200N-CT/D16-WF

——为家庭能源管理提供高精度、智能化解决方案 一、阳台光伏爆发的背景 在全球能源转型与碳中和目标的驱动下&#xff0c;阳台光伏正以革命性姿态重塑家庭能源消费模式。从欧洲的“微型发电站”到中国的“万亿蓝海”&#xff0c;这一创新技术不仅撬动了能源市场的结构性变革…

美团完整面经

面试岗位 面试的岗位 - 2025春季校招 【转正实习】软件服务工程师-后端方向&#xff08;成都 - 软硬件服务-SaaS事业部&#xff09; 一面&#xff08;业务初试 - 30min&#xff09; 问题 自我介绍 Java基础 HashMap底层用的数据结构是什么&#xff1f;是线程安全的吗&…

pysnmp 操作流程和模块交互关系的可视化总结

1. SNMP GET 操作序列图 #mermaid-svg-KALvv8WkHJTsNCeu {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-KALvv8WkHJTsNCeu .error-icon{fill:#552222;}#mermaid-svg-KALvv8WkHJTsNCeu .error-text{fill:#552222;str…

关于 /proc/net/tcp 与 /proc/$pid/net/tcp 的关系分析

关于 /proc/net/tcp 与 /proc/$pid/net/tcp 的关系分析 1. 基础概念 在 Linux 系统中&#xff0c;每个进程必定归属于一个且仅一个网络命名空间&#xff08;Network Namespace&#xff09;。这是 Linux 命名空间隔离机制的核心特性之一。 /proc/net/tcp 显示当前网络命名空间…

微信小程序 - 保存手机号等信息到通讯录

主要使用小程序 wx.addPhoneContact 这个api 一、界面 <view class"tab-item" bindtap"addToPhoneContacts">保存</view> 二、js 逻辑文件中 addToPhoneContacts() {wx.addPhoneContact({firstName: this.data.firstName, // 姓名mobilePh…

计算机视觉一些定义解析

1.GCT&#xff08;Gated Channel Transformation&#xff09; 定义 GCT&#xff08;Gated Channel Transformation&#xff09;是一种用于增强卷积神经网络特征提取能力的模块。它的核心思想是通过门控机制对特征图的通道进行动态调整&#xff0c;从而突出对任务更有帮助的特…

美团NoCode的Database 使用指南

系列文章目录 第一篇&#xff1a;美团NoCode设计网站的尝试经验分 第二篇&#xff1a;美团NoCode中的Dev Mode 使用指南 文章目录 系列文章目录Database 适用场景一、什么是 Database&#xff1f;二、准备流程1. 申请账号 三、使用流程1.申请资源的同时可搭建 NoCode 页面&…