HashMap总结使用+原理+面试

文章目录

      • 1.Hashmap的基本使用
        • 创建hashmap对象。
        • 遍历hashmap
        • 统计字母出现的次数用来投票计算
        • 返回JSON数据
      • 2.hashmap源码阅读
        • put源码阅读
      • 3. HashMap 面试题目
        • hashmap实现的原理
        • 什么时候数组需要进行扩容
        • hashmap怎么确定把数据放到那个节点的哪个位置。
        • 为什么用 `n - 1` 与运算?
        • 计算哈希值比较高效的哈希函数有哪些?
        • 是否是线程安全
        • 扩容机制
        • 负载因子为什么是0.75
        • HashMap 允许空键(`null` key)和空值(`null` value`)吗?

1.Hashmap的基本使用

创建hashmap对象。

创建一个hashmap对象, put方法进行往里面添加值。

Map<String,String> map=new HashMap<>();
map.put("111","abc");
map.put("112","abcd");
map.put("1123","abcde");
遍历hashmap
//直接遍历 keySet()
for (String key : map.keySet()) {System.out.println(key + " " + map.get(key));
}//如果只需要遍历value
for (String str : map.values()) {System.out.println(str);
}
//表示键值对的 Entry
for (Map.Entry<String, String> entry : map.entrySet()) {String key = entry.getKey();String value = entry.getValue();
}//stream流
map.entrySet().forEach(entry -> {System.out.println(entry.getKey() + " " + entry.getValue());
});
统计字母出现的次数用来投票计算

map集合的作用,统计字母出现的次数,比如有一串字母,想要统计每一个字母出现的次数可以使用map的结构。

public static void main(String[] args) {String str = "aaabbbcccdddeee";Map<Character,Integer> map=new HashMap<>();for (int i = 0; i < str.length(); i++) {char c=str.charAt(i);if (map.containsKey(c)){Integer value=map.get(c);map.put(c,value+1);}else{map.put(c,1);}}System.out.println(map);
}

返回JSON数据

在开发中map还可以用来存储数据,然后返回给前端。 如果后端返回的数据不固定,可以不设置VO类,直接用Map封装起来返回给前端,也是key-value的形式。

 Map<String, Object> map = new HashMap<>();
Integer id = selctedUser.getId();map.put("username", selctedUser.getUsername());map.put("id", id);String token = JWTUtil.createJWT("njitzx", 1000 * 3600 * 10, map);map.put("token", token);
@PostMapping("/login")
public Result login(@RequestBody User user) {Map<String, Object> map = userService.login(user);return Result.success(map);
}

2.hashmap源码阅读

hashmap的结构:

HashMap的成员变量:

transient Node<K,V>[] table; 哈希表结构中数组

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 默认容量是16

static final float DEFAULT_LOAD_FACTOR = 0.75f; //默认的加载因子 决定了hashmap的扩容时机.

hashmap每一个键值对对象中包含的元素:

链表中是Node

不是链表 TreeNode

hashmap创建的时候是懒加载创建,没有put的时候是空的。

直接创建一个空的HashMap,直接赋值因子,其他并没有创建。

public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // 0.75
}
put源码阅读

put方法调用,里面调用了putval方法。

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);//重复的数据覆盖
}

需要对key进行hash取值,放在数组的某个位置,这个hash函数就是为了分布均与一些,减少哈希碰撞。

static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
h ^ (h >>> 16):然后将原哈希值 h 与其右移后的值做异或操作。异或操作的效果是将低 16 位和高 16 位的值混合,生成一个更均匀的哈希值。这有助于降低哈希碰撞的概率,尤其是在数据较多时,可以避免哈希值的分布过于集中。

putval方法

1.添加元素的时候,数组的位置为null.

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length; //如果都为空 那么初始化这个数组i= (n - 1) & hash;  //计算索引的位置p = tab[i]if (p== null)  //hash值和n-l 与运算  不为空直接插入tab[i] = new Node(hash, key, value, null);  else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;  //添加的内容是相等的else if (p instanceof TreeNode) //p是否是树的节点e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {//如果不是红黑树中的节点  表示当前挂的是链表  按照链表的规则进行添加for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);//判断长度是否大于8   还有数组长度是否大于64 如果都满足 转为红黑树if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;  //结束循环}//出现了重复的内容  直接breakif (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;  //p往下移动一个   上面e=p.next  p=e;}}//p.next 复制给e了   当前需要覆盖元素的if (e != null) { // existing mapping for keyV oldValue = e.value;// onlyIfAbsent 是false 覆盖的    oldvalue不为空if (!onlyIfAbsent || oldValue == null)e.value = value;   afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;
}

3. HashMap 面试题目

hashmap实现的原理

jdk1.7之前hashmap实现的原理 是数组+链表实现的。 Node[] tables;

jdk1.8之后,当数据量比较少的时候时数组+链表,数据量比价大的时候时 数组+红黑树。当链表长度大于8,一个桶的元素超过了8会讲该桶转为红黑树。 当总的数量大于64,并且桶内的链表长度大于8的时候,也会进行链表到红黑树转换。

什么时候数组需要进行扩容

HashMap 有一个负载因子(默认值是 0.75),它决定了何时扩容。负载因子表示数组已填充的程度,当数组中已有的元素数量超过数组容量与负载因子乘积时,HashMap 会进行扩容。

threshold 这个用来记录阈值的。扩容一般是两倍进行扩容。

hashmap怎么确定把数据放到那个节点的哪个位置。

对于每一个key就是hashi值,然后和n-1进行与运算确定节点存在哪个位置,然后如果是单链表还需要遍历单链表去找到这个节点存储的位置。

为什么用 n - 1 与运算?

当数组的大小是 2 的幂时,n - 1 的二进制表示是由多个 1 组成的,这样可以保证哈希值的低位能够正确地映射到数组的索引。例如,如果 n 为 16,那么 n - 1 为 15(二进制:1111)。通过 & 运算,只保留哈希值的低 4 位,可以均匀地将哈希值分布到数组的各个位置。

计算哈希值比较高效的哈希函数有哪些?

拿到hashcode之后和 h>>16 向右移动16位进行或运算,生成一个更加均匀的哈希值。

static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
h ^ (h >>> 16):然后将原哈希值 h 与其右移后的值做异或操作。异或操作的效果是将低 16 位和高 16 位的值混合,生成一个更均匀的哈希值。这有助于降低哈希碰撞的概率,尤其是在数据较多时,可以避免哈希值的分布过于集中。
是否是线程安全

HashMap 不是线程安全的。在多线程环境下,如果多个线程同时访问并修改同一个 HashMap 实例,可能会导致数据不一致、死循环或者其他意外的行为 。

put 和 get 并发时会导致 get 到 null

HashMap 在扩容时,通常会执行以下步骤:

  1. 创建一个更大的数组(<font style="color:rgb(44,62,80);background-color:rgb(255,255,255);">newTab</font>),通常是原数组的两倍大。
  2. 将旧数组中的所有元素迁移到新数组中,这个过程是逐个元素搬迁的。
  3. 最后,<font style="color:rgb(44,62,80);background-color:rgb(255,255,255);">table = newTab</font> 将哈希表的引用指向新的数组。

在扩容过程中,可能会有多个线程同时访问 <font style="color:rgb(44,62,80);background-color:rgb(255,255,255);">HashMap</font>,其中一个线程(线程 1)正在执行扩容操作,将旧数组的数据迁移到新的数组上,而另一个线程(线程 2)则可能在此时进行 <font style="color:rgb(44,62,80);background-color:rgb(255,255,255);">get</font> 操作,导致其读取到不一致的数据(比如访问到了尚未迁移的部分或读取到了一个空的哈希表)。

不安全可以从原子性的角度去考虑,不是原子性的操作,不安全。

ConcurrentHashMap 是一种高性能的线程安全 Map 实现,适用于高并发场景。

它支持并发读取和部分更新(写操作是分段锁的),避免了全表锁定,提高了并发性能。

扩容机制

HashMap 的扩容是通过 resize 方法来实现的,该方法接收一个新的容量 newCapacity,然后将 HashMap 的容量扩大到 newCapacity:

  • 获取旧数组及容量:如果旧容量已经达到 HashMap 支持的最大容量 MAXIMUM_CAPACITY( 2 的 30 次方),就将新的阈值 threshold 调整为 Integer.MAX_VALUE(2 的 31 次方 - 1)
  • 创建新数组并转移元素:将旧数组 oldTable 中的元素转移到新数组 newTable 中。转移过程是通过调用 transfer 方法来实现的。该方法遍历旧数组中的每个桶,并将每个桶中的键值对重新计算哈希值后,将其插入到新数组对应的桶中。
  • 重新计算阈值 threshold:转移完成后,方法将 HashMap 内部的数组引用 table 指向新数组 newTable,并重新计算阈值 threshold:
负载因子为什么是0.75
  • 为了在时间和空间成本之间达到一个较好的平衡点,既可以保证哈希表的性能表现,又能够充分利用空间。
  • 如果负载因子过大,填充因子较多,那么哈希表中的元素就会越来越多地聚集在少数的桶中,这就导致了冲突的增加,这些冲突会导致查找、插入和删除操作的效率下降。
  • 如果负载因子过小,那么桶的数量会很多,虽然可以减少冲突,但会导致更频繁地扩容,在空间利用上面也会有浪费。
HashMap 允许空键(null key)和空值(null value`)吗?

允许一个 **null**HashMap 允许最多一个键为 null。这是因为 HashMap 使用 hash(null) 来计算其位置,通常会将 null 键存储在数组的第一个位置(索引为 0)。

如果key为null,那么就放在0的位置上面。

允许多个 **null**HashMap 允许多个键对应的值为 null。这意味着不同的键可以映射到 null 值,而不会相互干扰。

参考

https://blog.csdn.net/qq_49217297/article/details/126304736

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

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

相关文章

SpringMVC(四)响应

目录 数据处理及跳转 1. 结果跳转方式 ①.ModelAndView ②.ServletAPI 1、通过HttpServletResponse进行输出 2、通过HttpServletResponse实现请求转发 3、通过HttpServletResponse实现重定向 ③.SpringMVC 1.直接输出 2.请求转发 3.重定向 2.ResponseBody响应json数…

Mac软件介绍之录屏软件Filmage Screen

软件介绍 Filmage Screen 是一款专业的视频录制和编辑软件&#xff0c;适用于 Mac 系统 可以选择4k 60fps&#xff0c;可以选择录制电脑屏幕&#xff0c;摄像头录制&#xff0c;可以选择区域录制。同时也支持&#xff0c;简单的视频剪辑。 可以同时录制电脑麦克风声音 标准…

欧科云链研究院:ChatGPT 眼中的 Web3

编辑&#xff5c;OKG Research 转眼间&#xff0c;2024年已经进入尾声&#xff0c;Web3 行业经历了热闹非凡的一年。今年注定也是属于AI的重要一年&#xff0c;OKG Research 决定拉上 ChatGPT 这位“最懂归纳的AI拍档”&#xff0c;尝试把一整年的研究内容浓缩成精华。我们一共…

.NET 9.0 WebApi 发布到 IIS 详细步骤

微软表示&#xff0c;.NET 9 是迄今为止性能最高的 .NET 版本&#xff0c;对运行时、工作负载和语言方面进行了 1,000 多项与性能相关的改进&#xff0c;并采用了更高效的算法来生成更好的代码。 .NET 9 是 .NET 8 的继任者&#xff0c;特别侧重于云原生应用和性能。 作为标准期…

【通识安全】煤气中毒急救的处置

1.煤气中毒的主要症状与体征一氧化碳中毒&#xff0c;其中毒症状一般分为轻、中、重三种。 (1)轻度&#xff1a;仅有头晕、头痛、眼花、心慌、胸闷、恶心等症状。如迅速打开门窗&#xff0c;或将病人移出中毒环境&#xff0c;使之吸入新鲜空气和休息&#xff0c;给些热饮料&am…

ECCV`24 | 首次解决文本到3D NeRFs分解问题!港中文等提出DreamDissector

论文链接&#xff1a;https://arxiv.org/abs/2407.16260 亮点直击 据作者所知&#xff0c;作者是第一个解决文本到3D NeRFs分解问题的团队。 为了解决这个问题&#xff0c;本文引入了一个名为DreamDissector的新颖框架&#xff0c;包括一种新颖的神经类别场&#xff08;NeCF&a…

nginx-灰度发布策略(split_clients)

一. 简述&#xff1a; 基于客户端的灰度发布&#xff08;也称为蓝绿部署或金丝雀发布&#xff09;是一种逐步将新版本的服务或应用暴露给部分用户&#xff0c;以确保在出现问题时可以快速回滚并最小化影响的技术。对于 Nginx&#xff0c;可以通过配置和使用不同的模块来实现基于…

PCL点云库入门——PCL库点云特征之PFH点特征直方图(Point Feature Histograms -PHF)

1、算法原理 PFH点&#xff08;Point Feature Histogram&#xff09;特征直方图的原理涉及利用参数化查询点与邻域点之间的空间差异&#xff0c;并构建一个多维直方图以捕捉点的k邻域几何属性。这个高维超空间为特征表示提供了一个可度量的信息空间&#xff0c;对于点云对应曲面…

qml PathView详解

1、概述 PathView 是 Qt Quick 中一个非常强大的视图组件&#xff0c;它基于一个 Path 来展示视图项&#xff08;如 Item、Rectangle 等&#xff09;。PathView 可以让你按照定义的路径动态地显示多个元素&#xff0c;并且支持动画、滑动等功能。这个视图控件的最大特点是能够…

网络协议安全的攻击手法

1.使用SYN Flood泛洪攻击&#xff1a; SYN Flood(半开放攻击)是最经典的ddos攻击之一&#xff0c;他利用了TCP协议的三次握手机制&#xff0c;攻击者通常利用工具或控制僵尸主机向服务器发送海量的变源端口的TCP SYN报文&#xff0c;服务器响应了这些报文后就会生成大量的半连…

前端学习DAY31(子元素溢出父元素)

.box1{width: 200px;height: 200px;background-color: chocolate;} 子元素是在父元素的内容区中排列的&#xff0c;如果子元素的大小超过了父元素&#xff0c;则子元素会从 父元素中溢出&#xff0c;使用overflow属性设置父元素如何处理溢出的子元素 可选值&#xff1a;visible…

机器人手眼标定

机器人手眼标定 一、机器人手眼标定1. 眼在手上标定基本原理2. 眼在手外标定基本原理 二、眼在手外标定实验三、标定精度分析 一、机器人手眼标定 要实现由图像目标点到实际物体上抓取点之间的坐标转换&#xff0c;就必须拥有准确的相机内外参信息。其中内参是相机内部的基本参…

【前端下拉框】获取国家国旗

一、先看效果 二、代码实现&#xff08;含国旗&#xff09; <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…

Timer、Ticker使用及其注意事项

Timer、Ticker使用及其注意事项 在刚开始学习golang语言的时候就听说Timer、Ticker的使用要尤其注意&#xff0c;很容易出现问题&#xff0c;这次就来一探究竟。 本文主要脉络&#xff1a; 介绍定时器体系&#xff0c;并介绍常用使用方式和错误使用方式源码解读 timer、tic…

C++11——2:可变模板参数

一.前言 C11引入了可变模板参数&#xff08;variadic template parameters&#xff09;的概念&#xff0c;它允许我们在模板定义中使用可变数量的参数。这样&#xff0c;我们就可以处理任意数量的参数&#xff0c;而不仅限于固定数量的参数。 二.可变模板参数 我们早在C语言…

君正T41交叉编译ffmpeg、opencv并做h264软解,利用君正SDK做h264硬件编码

目录 1 交叉编译ffmpeg----错误解决过程&#xff0c;不要看 1.1 下载源码 1.2 配置 1.3 编译 安装 1.3.1 报错&#xff1a;libavfilter/libavfilter.so: undefined reference to fminf 1.3.2 报错&#xff1a;error: unknown type name HEVCContext; did you mean HEVCPr…

感知器的那些事

感知器的那些事 历史背景Rosenblatt和Minsky关于感知机的争论弗兰克罗森布拉特简介提出感知器算法Mark I感知机争议与分歧马文明斯基简介单层感知器工作原理训练过程多层感知器工作原理单层感知机 vs 多层感知机感知器模型(Perceptron),是由心理学家Frank Rosenblatt在1957年…

C语言:枚举类型

一、枚举类型的声明 枚举顾名思义就是一一列举。我们可以把可能的取值一一列举。比如我们现实生活中&#xff1a; 星期一到星期日是有限的7天&#xff0c;可以一一列举 &#xff1b;性别有&#xff1a;男、女、保密&#xff0c;也可以一一列举 &#xff1b;月份有12个月&#x…

25/1/6 算法笔记<强化学习> 初玩V-REP

我们安装V-REP之后&#xff0c;使用的是下面Git克隆的项目。 git clone https://github.com/deep-reinforcement-learning_book/Chapter16-Robot-Learning-in-Simulation.git 项目中直接组装好了一个机械臂。 我们先来分析下它的对象树 DefaultCamera:摄像机&#xff0c;用于…

CODESYS MODBUS TCP通信(AM400PLC作为主站通信)

禾川Q1 PLC MODBUS-TCP通信 禾川Q1 PLC MODBUS-TCP通信(CODESYS平台完整配置+代码)-CSDN博客文章浏览阅读17次。MATLAB和S7-1200PLC水箱液位高度PID控制联合仿真(MODBUSTCP通信)_将matlab仿真导入plc-CSDN博客文章浏览阅读722次。本文详细介绍了如何使用MATLAB与S7-1200PLC进行…