哈希表及其模拟实现

文章目录

  • 一、解决哈希冲突
    • 1.1闭散列
      • 1.1.1线性探测
      • 1.1.2二次探测
    • 1.2开散列
  • 二、模拟实现哈希表
  • 三、HashMap源码的一些相关内容

哈希(散列)方法:构造一种存储结构,通过某种函数使元素的存储位置与它的关键码之间能够建立 一 一 映射关系,那么在查找时通过该函数就可以很快找到该元素。
哈希(散列)方法中的函数称为哈希(散列)函数,构造出来的结构称为哈希表(散列表)。
例如在集合{1,2,3,4,5}中,哈希函数设置为hash(key)=key%capacity。
哈希函数的选择和数据整体元素有关,与单个元素没有直接关联。
对于哈希冲突,避免冲突的方法有:①设置合理的哈希函数 ②降低负载因子(即提高散列表的长度),负载因子=填入表中的元素个数/散列表的长度;解决冲突的方法有:①闭散列 ②开散列
采用哈希处理,一般所需空间都会比元素个数多,否则产生冲突的概率就比较大,所以哈希表相当于用空间来换取时间,哈希表的时间复杂度是O(1)。搜索时二叉搜索树比哈希表效率低,二叉搜索树的时间复杂度为O(logN)或O(N),哈希表的时间复杂度为O(1)。
hashCode()可以将对象转变为一个整数(为这个对象返回一个哈希值,hashCode()被支持使用到哈希表当中)。

一、解决哈希冲突

1.1闭散列

闭散列法,也叫开放定址法,发生哈希冲突时,如果哈希表未被装满,则把key存放到冲突位置的“下一个”空位置中去。

1.1.1线性探测

线性探测找“下一个”空位置:从发生冲突的位置开始,依次向后探测“下一个”空位置。
线性探测的缺点:
①把更多冲突的元素聚集在一起,这与其找下一个空位置有关
②不能随便删除哈希表1中已有的元素,如删除5,则55查找起来可能会受影响
在这里插入图片描述

1.1.2二次探测

为了避免把更多冲突的元素聚集在一起,二次探测找“下一个”空位置的方法为:Hi = (H0 + i ^2) % capacity 或 Hi = (H0 - i ^2) % capacity
在这里插入图片描述
与线性探测相比二次探测中冲突的元素较分开。
闭散列的缺点:空间利用率较低,也是哈希的缺陷。

1.2开散列

开散列法,也叫链地址法/开链法,使用哈希桶方式解决哈希冲突。哈希桶方式解决哈希冲突:哈希表是数组+链表的结构,当链表的长度超过8 && 数组的长度超过64时,链表变成红黑树。向哈希表中插入元素时JDK1.8之前是头插法,JDK1.8之后是尾插法。

二、模拟实现哈希表

这里用了开散列法解决哈希冲突。

//key-value模型
class Person {public String id;public Person(String id) {this.id = id;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return Objects.equals(id, person.id);}@Overridepublic int hashCode() {return Objects.hash(id);}
}
public class HashBucket<K, V> {private static class Node<K, V> {private K key;private V val;Node next;public Node(K key, V value) {this.key = key;this.val = value;}}private Node<K, V>[] array;private int usedSize;private static final double LOAD_FACTOR = 0.75;private static final int DEFAULT_SIZE = 8;//默认桶的大小public HashBucket() {array = (Node<K, V>[])new Node[10];}public void put(K key, V val) {int hash = key.hashCode();int index = hash % array.length;Node<K, V> cur = array[index];while(cur != null) {if(cur.key.equals(key)) {cur.val = val;return;}cur = cur.next;}Node<K, V> node = new Node<>(key, val);//头插法node.next = array[index];array[index] = node;usedSize++;if(loadFactor() >= LOAD_FACTOR) {resize();}}//扩容要把所有的元素重新进行哈希private void resize() {Node<K, V>[] tmpArr = new Node[array.length * 2];//遍历原来数组,将原来数组的元素重新哈希到新的数组当中。因为要遍历原来的数组,所以扩容时要申请一个新的数组for (int i = 0; i < array.length; i++) {Node<K, V> cur = array[i];while(cur != null) {Node<K, V> curNext = cur.next;int hash = cur.key.hashCode();int newIndex = hash % array.length;//头插法cur.next = tmpArr[newIndex];tmpArr[newIndex] = cur;cur = curNext;}}array = tmpArr;}private double loadFactor() {return usedSize * 1.0 / array.length;//散列表的载荷因子=填入表中的元素个数/散列表的长度}public V get(K key) {int hash = key.hashCode();int index = hash % array.length;Node<K, V> cur = array[index];while (cur != null) {if (cur.key.equals(key)) {return cur.val;}}return null;}
}

三、HashMap源码的一些相关内容

在这里插入图片描述
HashMap的其中一个构造方法HashMap(int initialCapacity, float loadFactor),这个构造方法里面有一个tableSizeFor(int cap)方法,tableSizeFor(int cap)方法的作用是返回一个接近目标容量的二次幂,如HashMap(int initialCapacity, float loadFactor)中的initialCapacity给了1000,则tableSizeFor(int cap)返回1024(返回大于1000的不返回小于1000的)。所以实例化HashMap时,initialCapacity给了1000,最后数组容量则是1024。
在这里插入图片描述
(h = key.hashCode()) ^ (h >>> 16)的目的是使关键字在哈希表中尽可能更均匀地分布。putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)中有(n - 1) & hash,n是数组长度,当(n - 1)的值比较小时,(n - 1)和hash二进制序列参与到计算中的只有低位。多个hash和(n - 1)进行计算,如果hash和(n - 1)这两者的值的二进制序列均是低位相同,高位不同的话,(n - 1) & hash计算出来的数组下标都是同一个,增加了冲突的几率,所以要用(h = key.hashCode()) ^ (h >>> 16)计算hash。当n是二次幂时,hash%n和hash&(n-1)的结果一样。&的结果使二进制序列更向0集中,|的结果使二进制序列更向1集中, ^的结果使二进制序列更加倾向保留参与计算的两者的二进制序列各自的特征。(h = key.hashCode()) ^ (h >>> 16)使hash的低位二进制序列既保留hashCode()二进制序列高位的特征,又保留了hashCode()二进制序列低位的特征。

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

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

相关文章

【JavaWeb】Tomcat底层机制和Servlet运行原理

&#x1f384;欢迎来到dandelionl_的csdn博文&#xff0c;本文主要讲解Java web中Tomcat底层机制和Servlet的运行原理的相关知识&#x1f384; &#x1f308;我是dandelionl_&#xff0c;一个正在为秋招和算法竞赛做准备的学生&#x1f308; &#x1f386;喜欢的朋友可以关注一…

HCIA实验二

实验要求&#xff1a; 1.R2为ISP&#xff0c;只能配置IP 2.R1-R2之间为HDLC封装 3.R2-R3之间为PPP封装&#xff0c;pap认证&#xff0c;R2为主认证方 4.R2-R4之间为PPP封装&#xff0c;chap认证&#xff0c;R2为主认证方 5.R1、R2、R3构建MGRE&#xff0c;仅R1的IP地址固定…

【Nginx12】Nginx学习:HTTP核心模块(九)浏览器缓存与try_files

Nginx学习&#xff1a;HTTP核心模块&#xff08;九&#xff09;浏览器缓存与try_files 浏览器缓存在 Nginx 的 HTTP 核心模块中其实只有两个简单的配置&#xff0c;这一块也是 HTTP 的基础知识。之前我们就一直在强调&#xff0c;学习 Nginx 需要的就是各种网络相关的基础知识&…

AndroidStudio设计一个计算器

界面设计 activity_calcuator.xml 设计&#xff1a; <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.android.com/apk/res-auto&qu…

3ds Max图文教程: 创建致命的冠状病毒动画

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 病毒建模 步骤 1 打开 3ds Max。 打开 3ds Max 步骤 2 在透视视口中创建一个半径为 50&#xff0c;线段为 20 的 GeoSphere。 创建地球 步骤 3 打开修改器列表并将置换修改器应用于地理 球。 置换…

【STL】模拟实现反向迭代器

目录 1. 读源码 2. 搭建框架 3. 迭代器的操作 operator*() operator->() operator() operator--() operator!() 4. 实现 list 的反向迭代器 5. 实现 vector 的反向迭代器 6. 源码分享 写在最后&#xff1a; 1. 读源码 我们之前实现的 vector&#xff0c;list…

类加载机制,类加载顺序

类加载顺序 ①类加载从上往下执行&#xff0c;依次执行静态的初始化语句和初始化块&#xff0c;而且类加载优先于对象创建。&#xff08;静态初始化语句和初始化块只加载一次&#xff09; ②创建本类的对象时&#xff0c;从上往下执行一次非静态的初始化语句和初始化块&#…

Unity《勇士传说》开发日记:如何制作可互动标识

要实现的需求&#xff1a; 在游戏当中&#xff0c;我们的主角走到宝箱前&#xff0c;可以将宝箱打开&#xff0c;走到洞穴口可以进入下一个场景&#xff0c;此时需要有个互动标识来提示用户。如图所示&#xff1a; 当角色走到宝箱前&#xff0c;弹出互动标识提示用户按下E键可…

关于idea如何成功运行web项目

导入项目 如图 依次选择 file - new - Project from Existing Sources 选择存放的项目目录地址 如图 导入完成 点击ok 如图 依次选择 Create project from existing sources 点击next如图 &#xff0c;此处默认即可 点击 next如图 点击next有该提示 是因为之前导入过…

jmeter接口测试、压力测试简单实现

jmeter测试的组件执行顺序&#xff1a; 测试计划—>线程组—>配置元件—>前置处理器—>定时器—>逻辑控制器—>取样器—>后置处理器—>断言—>监听器 组件的作用范围&#xff1a; 同级组件同级组件下的子组件父组件 目前市面上的三类接口 1、基…

10分钟带你实现一个Android自定义View:带动画的等级经验条

先展示一下静态效果图 介绍一下我们的实现流程&#xff1a; 首先整个经验条有一个圆角边框的背景打底&#xff1b;然后给经验条绘制一条轨道&#xff0c;让用户比较直观地看到总进度的长度&#xff1b;在轨道的上层绘制我们的渐变色经验条&#xff1b;在经验条的上层绘制等级…

用html+javascript打造公文一键排版系统8:附件及标题排版

最近工作有点忙&#xff0c;所 以没能及时完善公文一键排版系统&#xff0c;现在只好熬夜更新一下。 有时公文有包括附件&#xff0c;招照公文排版规范&#xff1a; 附件应当另面编排&#xff0c;并在版记之前&#xff0c;与公文正文一起装订。“附件”二字及附件顺序号用3号黑…

Python(四十六)列表

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

【Java基础教程】(四十八)集合体系篇 · 上:全面解析 Collection、List、Set常用子接口及集合元素迭代遍历方式~【文末送书】

Java基础教程之集合体系 上 &#x1f539;本章学习目标1️⃣ 类集框架介绍2️⃣ 单列集合顶层接口&#xff1a;Collection3️⃣ List 子接口3.1 ArrayList 类&#x1f50d; 数组&#xff08;Array&#xff09;与列表&#xff08;ArrayList&#xff09;有什么区别?3.2 LinkedL…

在 ArcGIS Pro 中使用 H3 创建蜂窝六边形

H3是Uber开发的分层索引系统,它使用六边形来平铺地球表面。H3在二十面体(一个具有20个三角形面和12个顶点的形状)上构建其六边形网格。由于仅用六边形不可能平铺二十面体,因此每个分辨率需要12个五边形来完成网格。分层索引网格意味着每个六边形都可以细分为子单元六边形。…

给jupter设置新环境

文章目录 给jupternotebook设置新环境遇到的报错添加路径的方法 给jupternotebook设置新环境 # 先在anaconda界面新建环境 conda env list # 查看conda prompt下的有的环境变量 带星号的是当前活跃的 activate XXXX pip install ipykernel ipython ipython kernel install --u…

如何安装mmcv?官网解答

pip install -U openmim mim install mmcv

【高分论文密码】大尺度空间模拟预测与数字制图教程

详情点击链接&#xff1a;【高分论文密码】大尺度空间模拟预测与数字制图 一&#xff0c;R语言空间数据及数据挖掘关键技术 1、R语言空间数据及应用特点 1)R语言基础与数据科学 2)R空间矢量数据 3)R栅格数据 2、R语言空间数据挖掘关键技术 二&#xff0c;R语言空间数据高…

素描基础知识

素描基础入门 1.基础线条 1.1 握笔姿势及长线条 2.排线 2.1 不同姿势画排线 2.1.1 姿势画排线 2.1.2 用手腕画排线 2.1.3 小拇指画排线 2.1.4 叠加排线 2.1.5交叉排线 2.2 纸张擦法 2.3 排线学习榜样 2.4 四种常见的排线 3、定向连线 4、一点透视 4.1 透视的规律 4.2 焦点透视…