散列表的小想法

了解就好,已经有极其优秀的轮子了,在 java 中类似的数据结构是 HashMap

散列表的实现方式

散列表一般使用 数组+桶(链表/树) 实现。

当两个数据的哈希值相同->存到同一个下标对应的位置->一个下标只有一个位置,怎么解决?

在这个位置建一个链表,将数据存储在链表中,如下图。

缺点:

当存储空间有限的时候,会出现大量数据堆积在一个位置,但可通过计算添装因子(散列表中已有的数据/位置总数)确定要不要扩容,添装因子超过 0.7 要调整散列表的长度。

散列表的应用:

  • 模拟映射关系、

    例如:可以使用散列表来存储商店的商品和价格。

    散列表内部 通过一个数组 实现,并依赖散列函数 将商品名(如'水')转换为数组索引,在对应索引存储位置上存储 水的价格。

    当输入'水'时,散列表会先通过散列函数 找到存储位置(即数组下标) ,再从该位置取出对应的值(价格),最终返回 价格。

    散列函数:hash("水") → 数组索引(如 3)。 散列表:根据索引找到桶,再从桶中匹配键 "水",最终返回对应的值 2.5。

    css 复制代码
        A[输入商品名 '水'] --> B[散列函数 hash'水' ]
        B --> C[输出索引 3]
        C --> D[定位数组索引3的桶]
        D --> E{桶内搜索键 '水'}
        E -->|匹配成功| F[返回价格 2.5]
  • 防止重复数据的出现

  • 缓存

    例如:浏览器的缓存机制

    浏览器缓存的机制是一套 "资源存储、有效性校验、优先级读取" 的完整流程,核心是通过 "规则约定" 和 "HTTP 协议字段",决定 "哪些资源要缓存、缓存多久、下次请求时是否复用缓存"。其机制可拆解为 "缓存的分类""缓存的存储位置""缓存的校验规则" 三个核心维度,三者共同构成浏览器缓存的完整逻辑。

    一、第一步:缓存的分类 ------ 按 "是否向服务端确认" 划分

    浏览器缓存并非 "一刀切" 存储所有资源,而是根据 "是否需要每次请求前向服务端确认有效性",分为强缓存(强制缓存)协商缓存(对比缓存) 两类 ------ 二者的优先级和触发逻辑不同,共同决定资源的复用策略。

    1. 强缓存(强制缓存):"本地有有效缓存,就直接用,不发请求"

    强缓存是优先级最高 的缓存机制:当浏览器第一次下载资源后,会在本地记录 "缓存有效期";下次需要该资源时,先检查本地缓存是否在有效期内 ------ 若有效,完全不向服务端发送请求 ,直接从本地加载资源(浏览器控制台中显示 200 OK (from cache)200 OK (memory cache))。

    强缓存的核心是 "用本地缓存跳过网络请求",效率极高,其有效期通过 HTTP 响应头字段 控制,主要有两个:

    响应头字段 作用 兼容性与特点
    Cache-Control 现代浏览器首选,通过指令明确缓存规则(优先级高于 Expires 支持 HTTP/1.1,指令灵活,如:- max-age=3600:缓存有效期 3600 秒(1 小时);- public:允许中间节点(如 CDN)缓存该资源;- private:仅允许浏览器本地缓存(禁止 CDN 等缓存);- no-cache:不启用强缓存,直接进入协商缓存;- no-store:完全禁止缓存(任何情况下都不存储资源)。
    Expires 早期 HTTP/1.0 字段,通过 "绝对时间" 指定缓存过期时间 兼容性好,但存在缺陷:- 时间基于 "服务端时间",若客户端与服务端时间不一致(如客户端时间调快),会导致缓存提前失效或过期后仍被使用;- 优先级低于 Cache-Control(若两者同时存在,以 Cache-Control 为准)。

    示例 :服务端返回一张图片的响应头,通过 Cache-Control 设置强缓存 1 小时:

    makefile 复制代码
    HTTP/1.1 200 OK
    Cache-Control: max-age=3600
    Content-Type: image/png
    Content-Length: 10240

    浏览器下次请求该图片时,若距离上次下载时间小于 1 小时,直接用本地缓存,不发请求。

    2. 协商缓存(对比缓存):"本地有缓存,但需先问服务端'是否可用'"

    当强缓存失效(如 max-age 过期、资源设置了 no-cache)时,浏览器会进入协商缓存流程:先向服务端发送请求,携带 "本地缓存的资源标识" ,由服务端判断本地缓存是否仍有效 ------ 若有效,服务端返回 304 Not Modified,浏览器直接复用本地缓存;若无效,服务端返回 200 OK 并携带新资源,浏览器更新本地缓存。

    协商缓存的核心是 "服务端决策缓存有效性",避免 "强缓存过期但资源未更新" 的冗余下载,其关键是 "资源标识" 的传递与对比,主要通过两组 HTTP 头字段实现:

    客户端请求头(携带本地标识) 服务端响应头(返回资源标识) 标识含义与对比逻辑
    If-Modified-Since Last-Modified 基于 "资源修改时间" 的标识:1. 首次请求:服务端返回 Last-Modified: Wed, 12 Oct 2024 10:00:00 GMT(资源最后修改时间),浏览器存储该时间;2. 再次请求:浏览器携带 If-Modified-Since: Wed, 12 Oct 2024 10:00:00 GMT(本地存储的时间);3. 服务端对比:若资源最后修改时间 > 请求头时间(资源已更新),返回 200+新资源;若 ≤,返回 304
    If-None-Match ETag 基于 "资源内容哈希" 的标识(优先级高于 Last-Modified):1. 首次请求:服务端计算资源内容的哈希值(如 MD5),返回 ETag: "abc123def456",浏览器存储该哈希;2. 再次请求:浏览器携带 If-None-Match: "abc123def456"(本地存储的哈希);3. 服务端对比:若当前资源哈希 ≠ 请求头哈希(资源内容已变),返回 200+新资源;若 =,返回 304

    为什么需要 ETag

    Last-Modified 存在缺陷:若资源内容未变,但修改时间被误改(如服务端重新部署但资源未更新),会导致 Last-Modified 变化,协商缓存失效(冗余下载);而 ETag 基于内容哈希,只要内容不变,哈希就不变,能更精准判断资源是否更新。

    二、第二步:缓存的存储位置 ------"不同资源存在不同地方"

    浏览器会根据资源的 "使用频率" 和 "大小",将缓存存储在不同位置,读取速度和生命周期不同,优先级从高到低为:Memory Cache(内存缓存)→ Disk Cache(磁盘缓存)→ Service Worker Cache(服务工作线程缓存)

    存储位置 读取速度 存储内容 生命周期
    Memory Cache(内存缓存) 最快(微秒级) 最近访问的 "小体积、高频使用" 资源,如:- 页面渲染必需的 CSS、JS;- 刚加载的小图片、图标。 短(浏览器关闭后清空):内存空间有限,当内存不足时,会优先淘汰 "不常用的内存缓存"。
    Disk Cache(磁盘缓存) 较慢(毫秒级) 大部分缓存资源,如:- 大体积资源(如大图、视频片段);- 非即时渲染但需长期复用的资源(如公共 CSS、第三方库)。 长(按缓存规则过期,浏览器关闭后仍保留):磁盘空间大,存储更持久,是浏览器缓存的核心。
    Service Worker Cache(服务工作线程缓存) 中等 由前端代码(Service Worker)主动控制的缓存,如:- PWA(渐进式 Web 应用)的离线资源;- 自定义需要 "离线可用" 的资源(如文档、静态页面)。 由代码控制(需手动通过 API 删除或更新):完全脱离浏览器默认缓存逻辑,是 "可编程缓存"。

    读取优先级逻辑:当浏览器需要某资源时,会按 "Memory Cache → Disk Cache → Service Worker Cache" 的顺序查找 ------ 若内存中有,直接用;若没有,查磁盘;若磁盘也没有,再通过 Service Worker 判断;都没有则发起网络请求。

    三、第三步:缓存的完整流程 ------"从首次请求到再次请求"

    结合 "分类" 和 "存储位置",浏览器缓存的完整流程可分为 "首次请求" 和 "再次请求" 两个阶段,清晰体现其机制:

    1. 首次请求资源(无本地缓存)

    1. 浏览器向服务端发送 HTTP 请求(如请求 index.css);
    2. 服务端处理请求,返回资源及 "缓存规则头"(如 Cache-Control: max-age=3600ETag: "abc123");
    3. 浏览器接收资源,按 "存储位置规则" 将资源存入 Memory Cache(即时使用)和 Disk Cache(长期复用);
    4. 浏览器渲染页面,使用刚下载的资源。

    2. 再次请求同一资源(有本地缓存)

    1. 浏览器先检查 Memory Cache :若有且未失效,直接使用(返回 200 from memory cache);

    2. 若 Memory Cache 无,检查Disk Cache:

      • 若 Disk Cache 无,直接发起网络请求(同首次请求);

      • 若 Disk Cache 有,先触发强缓存校验:

        • 若强缓存有效(max-age 未过期 / Expires 未到),直接使用 Disk Cache(返回 200 from disk cache);

        • 若强缓存失效,触发协商缓存校验:

          ① 浏览器携带 "本地标识"(If-Modified-Since If-None-Match)向服务端发送请求;

          ② 服务端对比标识:

          • 若标识一致(资源未更新),返回 304 Not Modified,浏览器复用 Disk Cache;
          • 若标识不一致(资源已更新),返回 200 OK + 新资源 + 新缓存规则,浏览器更新 Disk Cache,再使用新资源。

    四、特殊场景:缓存的 "失效与更新"

    浏览器缓存并非永久有效,当资源需要更新时(如网站改版,CSS 样式变化),需通过以下方式让缓存失效,确保用户获取最新资源:

    1. 资源路径加 "版本号" 或 "哈希值" :最常用的方式,如将 style.css 改为 style.v2.cssstyle.abc123.css(哈希值随内容变化)------ 由于路径不同,浏览器会认为是 "新资源",直接发起网络请求,避免复用旧缓存。(这也是前端工程化工具如 Webpack、Vite 自动处理缓存的核心原理)。

    2. 设置 Cache-Control: no-cacheno-store

      • 对 "频繁更新的资源"(如首页 HTML、实时数据接口),设置 no-cache(跳过强缓存,每次都走协商缓存);
      • 对 "绝对不能缓存的资源"(如用户登录态、验证码图片),设置 no-store(完全不存储缓存,每次都发新请求)。
    3. 服务端更新资源时同步更新 ETagLast-Modified :当资源内容变化时,服务端会生成新的 ETag(内容哈希变化)或更新 Last-Modified(修改时间变化),协商缓存时会触发 200 响应,更新本地缓存。

散列表的核心元素 快递柜的对应元素 映射逻辑
Key(键) 取件码(如 12-3) 你手中的取件码就是 "键"
哈希函数 快递柜的地址规则 比如 "取件码前两位是柜号,后一位是格子号",就是 "哈希函数",通过取件码计算出格子地址
哈希地址 快递格子位置(如 12 号柜 3 号格) 计算出的 "地址",直接定位存储位置
Value(值) 你的快递包裹 地址对应的 "目标内容"
哈希冲突 两个取件码算出同一格子 此时快递员会用 "备用格子"(类似散列表的冲突解决)
相关推荐
Code小翊4 小时前
堆的基础操作,C语言示例
java·数据结构·算法
余俊晖4 小时前
如何让多模态大模型学会“自动思考”-R-4B训练框架核心设计与训练方法
人工智能·算法·机器学习
Emilia486.4 小时前
【Leetcode&nowcode&数据结构】顺序表的应用
数据结构·算法·leetcode
一水鉴天4 小时前
整体设计 逻辑系统程序 之27 拼语言整体设计 9 套程序架构优化与核心组件(CNN 改造框架 / Slave/Supervisor/ 数学工具)协同设计
人工智能·算法
小年糕是糕手4 小时前
【数据结构】双向链表“0”基础知识讲解 + 实战演练
c语言·开发语言·数据结构·c++·学习·算法·链表
PyHaVolask5 小时前
数据结构与算法分析
数据结构·算法·图论
小王C语言5 小时前
封装红黑树实现mymap和myset
linux·服务器·算法
大佬,救命!!!5 小时前
算法实现迭代2_堆排序
数据结构·python·算法·学习笔记·堆排序
天桥下的卖艺者6 小时前
R语言手搓一个计算生存分析C指数(C-index)的函数算法
c语言·算法·r语言