Java实现数据结构哈希表

哈希表

概述

给美分数据分配一个编号,放入表格(数组)

建立编号与表格索引的关系,将来就可以通过编号快速查找数据

  1. 理想情况编号当唯一,数组能容纳所有数据
  2. 现实是不能说为了容纳所有数据造一个超大数组,编号也可能重复

解决

  1. 有限长度的数组,以[拉链]方式存储数据
  2. 允许编号适当重复,通过数据自身来进行区分

代码

基础代码

public class HahTable {//节点类static class Entry{int hash;   //哈希码Object key; //键Object value;   //值Entry next;public Entry(int hash, Object key, Object value) {this.hash = hash;this.key = key;this.value = value;}}//创建一个数组,数组里面装的是Entry节点,也就是链表的头节点Entry[] table = new Entry[16];int size = 0;   //元素个数
}

获取

这里实际上可以使用取余运算,如果key值是9,8对数组的长度进行求余数,可以得出来是0,所以可以在数组0的那根链表上继续进行查找

可以看出索引3位置处就没有元素,返回的就是null

//根据hash码获取valueprivate Object get(int hash,Object key){//hash码与数组的长度减一,实际上取到的就是索引值int index = hash & (table.length - 1);//如果数组的该索引位置处没有值的话,也不会有对应的value值,所以可以直接返回if(table[index] == null){return null;}//将table数组处index索引位置处的链表找到Entry p = table[index];//遍历链表while (p != null){//如果链表的key和传过来的key值相等的话可以直接将key值当值节点返回if(p.key.equals(key)){return p;}p = p.next;}//找到最后都没有的话,返回null值return null;}

插入

插入实际上也是,索引3位置处并没有内容,所以可以直接插入,而其他索引位置处就需要找到链表进行对应的位置插入了

 //向hash表存入新的key,value,如果key重复,则更新valueprivate void put(int hash,Object key,Object value){//hash码与数组的长度减一,实际上取到的就是索引值int index = hash & (table.length - 1);//接下来会有两种情况//第一种:index索引位置处有空位置,直接新增if(table[index] == null){table[index] = new Entry(hash,key,value);}else{//第二种:index索引位置处没有空位,沿着链表查找,有key值的话,更新,没有的话新增Entry p = table[index];//遍历链表while (true){//如果链表的key和传过来的key值相等的话可以直接将key值当值节点返回if(p.key.equals(key)){p.value = value;return;}if(p.next == null){break;}p = p.next;}//此时的p就是最后一个节点p.next = new Entry(hash,key,value);}size++;}

删除

//根据hash码删除,返回删除的valueprivate Object remove(int hash,Object key){//hash码与数组的长度减一,实际上取到的就是索引值int index = hash & (table.length - 1);//如果数组的index位置处为null,直接返回nullif(table[index] == null){return null;}//找到数组索引位置处的链表Entry p = table[index];Entry prev = null;//设置删除节点的父节点while (p != null){if(p.key.equals(key)){//找到了删除节点//如果删除的是头节点的话,将数组索引位置处的元素设置为链表头节点的下一个元素if(prev == null){table[index] = p.next;}else{//如果不是的话,将父节点的next指向儿子节点的next,这样就把儿子节点忽略掉了prev.next = p.next;}return p.value;}prev = p;   //将当前节点设置称为删除节点的父节点p = p.next;}return null;}

此时会出现一个问题,就是如果说元素内容并不多,但是数组长度会很长则造成了空间的浪费,如果说数组长度不长,但是元素足够多就会导致增删改查的效率变低,所以需要在合适的时候对数组进行扩容,这里就引出了一个新的名词负载因子

扩容

负载因子公式如下图所示

  • 从上面可以看出n代表的是元素的个数,m代表的是数组的长度,他们的比值最好在4分之3左右
  • 扩容之后一般采取的办法是容量进行翻倍

 float loadFactor = 0.75f;//负载因子大小  12称为阈值int threshold =(int) (loadFactor * table.length);//这个实际上计算的实际上就是阈值//向hash表存入新的key,value,如果key重复,则更新valueprivate void put(int hash,Object key,Object value){//hash码与数组的长度减一,实际上取到的就是索引值int index = hash & (table.length - 1);//接下来会有两种情况//第一种:index索引位置处有空位置,直接新增if(table[index] == null){table[index] = new Entry(hash,key,value);}else{//第二种:index索引位置处没有空位,沿着链表查找,有key值的话,更新,没有的话新增Entry p = table[index];//遍历链表while (true){//如果链表的key和传过来的key值相等的话可以直接将key值当值节点返回if(p.key.equals(key)){p.value = value;return;}if(p.next == null){break;}p = p.next;}//此时的p就是最后一个节点p.next = new Entry(hash,key,value);}size++;if(size > threshold){resize();}}/*** 扩容方法*/private void resize() {Entry[] newTable = new Entry[table.length << 1];    //移位比直接乘以2效率更高for (int i = 0; i < table.length; i++) {Entry p = table[i];if(p != null){//拆分链表,移动到新的数组/*拆分规律* 一个链表最多拆成两个* hash & table.length == 0 的一组* hash & table.length != 1 的一组*/}}table = newTable;//扩容完成之后将新数组赋值给旧数组threshold = threshold =(int) (loadFactor * table.length);}

讲到这里就需要科普一个知识了
0 (二进制: 0000) & (按位与) 8 (二进制: 1000)   结果0

8 (二进制: 1000) & (按位与) 8 (二进制: 1000)    结果8

16 (二进制: 10000) & (按位与) 8 (二进制: 1000)  结果0

24 (二进制: 11000) & (按位与) 8 (二进制: 01000)  结果8

可以看出如果最后四位的值相等的时候结果为8,否则结果为0

 /*** 扩容方法*/private void resize() {// 创建新的Entry数组,长度为原数组的两倍(通过位运算效率更高)Entry[] newTable = new Entry[table.length << 1];// 遍历原数组for (int i = 0; i < table.length; i++) {Entry p = table[i];// 如果当前位置有链表if(p != null){// 拆分链表,移动到新的数组/*拆分规律* 一个链表最多拆成两个* hash & table.length == 0 的一组* hash & table.length != 1 的一组*/// 创建两个新的链表,一个是a,另一个是bEntry a = null;Entry b = null;Entry aHead = null;Entry bHead = null;// 递归遍历链表while (p != null){// 如果当前节点的hash与数组的长度 == 0的时候添加到a,否则添加到bif((p.hash & table.length) == 0){if(a != null){a.next = p;}else{aHead = p;}// 分配到aa = p;}else{if(b != null){b.next = p;}else{bHead = p;}// 分配到bb = p;}// 进行递归遍历p = p.next;}// 将链表a添加到新数组的原位置if(a != null ){a.next = null;newTable[i] = aHead;}// 将链表b添加到新数组的原位置的后半部分if(b != null ){b.next = null;newTable[i + table.length] = bHead;}}}// 扩容完成之后将新数组赋值给旧数组table = newTable;// 更新阈值threshold = (int) (loadFactor * table.length);}

哈希算法

概述

hash算法是将任意对象,分配一个编号的过程,其中编号是一个有限范围内的数字(如int范围内),hash算法是一种将任意长度的数据通过一个算法,变成固定长度数据的过程,这个固定长度的数据就是hash值。hash算法可以将任意大小的数据压缩到固定大小的值。常见的hash算法有MD5、SHA1、SHA256、SHA512、CRC32等。其中,MD5和SHA系列算法是最常用的hash算法。这些算法在计算hash值时,都考虑了原始数据的每一个字节,一旦改动原始数据的任何一个字节,所得到的hash值都会有明显的不同。因此,hash算法被广泛应用于数据完整性校验和加密等方面。

在java里面可以调用对象的HashCode方法来进行获取

字符串生成hash的方法

public static void main(String[] args) {String str = "abc";System.out.println(str.hashCode());System.out.println("==================");int hash = 0;for (int i = 0; i < str.length(); i++) {char c = str.charAt(i);hash = hash * 31 +c;}System.out.println(hash);}

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

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

相关文章

【IO流】IOException IO流异常

IOException IO流异常 1. 概述2. try...catch异常处理2.1 基础做法2.2 JDK7方案2.3 JDK9方案 3. 注意事项 异常 概括 1. 概述 IOException&#xff08;Input/Output Exception&#xff0c;输入/输出异常&#xff09;是 Java 编程中常见的异常类型之一。它是 java.io 包中定义的…

48 slab 的实现

前言 这里说的是 内核中分配小对象的一种内存分配方式 slab 呵呵 经典程度不必多说了, 内核使用的大多数数据结构 基本上是基于 slab 进行内存分配的 这里 我们来看一下 slab 如何分配对象 几个分配层级, c->free_list, c->page, c->partial, new_slab 1. 先…

查看 PyCharm 代码文件目录位置

查看 PyCharm 代码文件目录位置 1. Show in Files2. Copy PathReferences 1. Show in Files right click -> Show in Files / Show in Explorer 即可打开目录 2. Copy Path right click -> Copy Path 即可复制目录或文件路径 References [1] Yongqiang Cheng, http…

通过Linux终端搭建基于HTTP隧道的文件传输系统

嘿&#xff0c;Linux小侠们&#xff0c;准备好挑战一项酷炫的任务了吗&#xff1f;今天我们要一起通过Linux终端搭建一个基于HTTP隧道的文件传输系统&#xff0c;让我们的文件在网络的海洋中畅游无阻&#xff01; 在开始之前&#xff0c;让我们先来想象一下这个场景&#xff1…

自建AWS S3存储服务

unsetunset前言unsetunset AWS S3&#xff08;Amazon S3&#xff0c;全名为亚马逊简易存储服务&#xff09;&#xff0c;是亚马逊公司利用其亚马逊网络服务系统所提供的网络在线存储服务。我常用的很多SaaS服务中提供的文件存储功能&#xff0c;底层也都是AWS S3&#xff0c;比…

线代:认识行列式、矩阵和向量

本文主要参考的视频教程如下&#xff1a; 8小时学完线代【中国大学MOOC*小元老师】线性代数速学_哔哩哔哩_bilibili 另外这个视频可以作为补充&#xff1a; 【考研数学 线性代数 基础课】—全集_哔哩哔哩_bilibili 行列式的概念和定义 一般会由方程组来引出行列式 比如一个二阶…

【Python】测量WAV文件播放时长

问题 windows播放WAV音频文件&#xff0c;一般使用API函数&#xff0c;如PlaySound。实际使用发现&#xff0c;从调用PlaySound到实际开始播放存在200ms以上的延时&#xff0c;在游戏编程中音效实时性是个需要解决的问题。 本文主要讨论&#xff0c;windows播放WAV文件的衍生…

后端开发怎么学?

后端开发怎么学&#xff1f; 后端开发可以简单地理解为与前端开发相对应的开发方向。前端开发主要负责构建用户界面、维护用户体验等方面的工作&#xff0c;而后端开发则主要负责处理数据、逻辑和算法等方面的工作。后端开发旨在为前端应用程序提供支持&#xff0c;以帮助实现可…

微信小程序之开发会议OA项目

目录 前言 本篇目标 首页 会议 投票 个人中心 会议OA项目-首页 配置 tabbar mock工具 page swiper 会议信息 会议OA项目-会议 自定义tabs组件 会议管理 会议OA项目-投票 会议OA项目-个人中心 前言 文章含源码资源&#xff0c;投票及个人中心详细自行查看…

【开源】SpringBoot框架开发服装店库存管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 角色管理模块2.3 服装档案模块2.4 服装入库模块2.5 服装出库模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 角色表3.2.2 服装档案表3.2.3 服装入库表3.2.4 服装出库表 四、系统展示五、核心代码5.…

VTK Python PyQt 监听键盘 控制 Actor 移动 变色

KeyPressInteractorStyle 在vtk 中有时我们需要监听 键盘或鼠标做一些事&#xff1b; 1. 创建 Actor&#xff1b; Sphere vtk.vtkSphereSource() Sphere.SetRadius(10)mapper vtk.vtkPolyDataMapper() mapper.SetInputConnection(Sphere.GetOutputPort()) actor vtk.vtkAc…

立创EDA专业版快速使用和一些快捷键

立创EDA专业版 介绍原理图PCB快捷键原理图PCB 介绍 器件 符号 封装 3D模型 属性 仅支持放置器件在原理图或PCB 一个Board(板&#xff09;只能有一个原理图和PCB&#xff0c;一个原理图可以有多页 原理图 网络标签 是给连接线命名 新建图页 如果一个图页上画不下&#…

第4讲 小程序首页实现

首页 create.vue <template><view class"vote_type"><view class"vote_tip_wrap"><text class"type_tip">请选择投票类型</text><!-- <text class"share">&#xe739;分享给朋友</text&g…

STM32中断定时器的使用

使用systimer来产生较为精确的定时&#xff0c;之前使用for循环来产生。 用示例工程时产生错误&#xff0c;原因是调用F103的3种容量器件&#xff0c;需要更换S汇编头函数。 另外在工程设置中&#xff0c;需要把HD设置为MD&#xff0c;重新编译即可成功。

二叉树基础总结

目录 树的定义&#xff1a; 深度和高度&#xff1a; 二叉树 由来 二叉树种类&#xff1a; 满二叉树&#xff1a; 完全二叉树&#xff1a; 严格二叉树&#xff08;Strict Binary Tree&#xff09;&#xff1a; 平衡二叉树&#xff08;Balanced Binary Tree&#xff09;…

DoRA(权重分解低秩适应):一种新颖的模型微调方法

来自&#xff1a;小互 DoRA&#xff08;权重分解低秩适应&#xff09;&#xff1a;一种新颖的模型微调方法 DoRA在LoRA的基础上进一步发展&#xff0c;通过将预训练权重分解为“幅度”和“方向”两个部分进行微调。 这种权重分解方法允许DoRA更精细地控制模型的学习过程&…

基于函数计算AIGC生成图应用

目录 基于函数计算部署AIGC应用的主要步骤 创建Stable Diffusion模型的应用 访问应用实现文字生图 函数的查看与管理 基于函数计算部署AIGC应用的主要步骤 用函数计算实现AIGC只要简单的三步&#xff0c;分别是创建应用、运行应用及查看管理。 创建Stable Diffusion模型的应…

【大厂AI课学习笔记】【2.2机器学习开发任务实例】(3)数据准备和数据预处理

项目开始&#xff0c;首先要进行数据准备和数据预处理。 数据准备的核心是找到这些数据&#xff0c;观察数据的问题。 数据预处理就是去掉脏数据。 缺失值的处理&#xff0c;格式转换等。 延伸学习&#xff1a; 在人工智能&#xff08;AI&#xff09;的众多工作流程中&#…

Python如何实现定时发送qq消息

因为生活中老是忘记各种事情&#xff0c;刚好又在学python&#xff0c;便突发奇想通过python实现提醒任务的功能&#xff08;尽管TIM有定时功能&#xff09;&#xff0c;也可定时给好友、群、讨论组发送qq消息。其工作流程是&#xff1a;访问数据库提取最近计划——>根据数据…

打开ps显示找不到dll怎么办?这四种方法可快速修复

在计算机操作系统中&#xff0c;当执行某程序或运行特定软件时&#xff0c;如果系统提示“ps显示找不到dll文件”&#xff0c;这其实是一个较为常见的问题现象。动态链接库&#xff08;DLL&#xff09;文件是Windows操作系统中不可或缺的重要组件&#xff0c;它包含了大量可被多…