详细介绍:【数据结构】哈希表(Hash Table)详解

news/2025/9/27 16:59:29/文章来源:https://www.cnblogs.com/tlnshuju/p/19115310

详细介绍:【数据结构】哈希表(Hash Table)详解

2025-09-27 16:57  tlnshuju  阅读(0)  评论(0)    收藏  举报

目录

1. 核心概念

核心特性:

2. 哈希表的工作原理

2.1 基本工作流程

2.2 简单示例

3. 哈希冲突(Hash Collision)

冲突示例:

4. 解决哈希冲突的方法

4.1 链地址法(Separate Chaining)★最常用

4.2 开放地址法(Open Addressing)

4.2.1 线性探测(Linear Probing)

4.2.2 平方探测(Quadratic Probing)

4.2.3 双重散列(Double Hashing)

5. 哈希函数的设计

5.1 常见哈希函数设计

6. Java中的HashMap实现

6.1 HashMap的核心结构

6.2 HashMap的重要参数

6.3 HashMap的扩容机制

7. 时间复杂度分析

8. 哈希表的应用场景

8.1 缓存系统(LRU Cache)

8.2 词频统计

8.3 快速查找表

8.4 重复检测

8.5 两数之和问题

9. 哈希表实现示例

9.1 简单哈希表实现

10. 哈希表相关问题与解决方案

10.1 哈希攻击(Hash Flooding Attack)问题与解决方案

问题描述

攻击原理分析

解决方案

10.2 内存使用优化问题与解决方案

问题分析

解决方案

10.3 一致性哈希(Consistent Hashing)问题与解决方案

分布式环境中的问题

一致性哈希解决方案

实际应用场景

哈希表的优势:

哈希表的劣势:

Java中的选择:


1. 核心概念

​哈希表​​ 是一种通过​​键(Key)​​ 直接访问​​值(Value)​​ 的数据结构。它的核心思想是:​​通过哈希函数将键映射到数组的特定位置,从而实现快速访问​​。

核心特性:

  1. ​键值对存储​​:存储的是键值对(Key-Value Pair)

  2. ​快速访问​​:平均情况下,插入、删除、查找的时间复杂度都是 ​​O(1)​

  3. ​无序性​​:元素没有固定的顺序(某些实现保持插入顺序)

  4. ​键的唯一性​​:每个键在表中是唯一的

2. 哈希表的工作原理

2.1 基本工作流程

输入键(Key)↓
哈希函数(Hash Function)↓
哈希值(Hash Code)↓
数组索引(Index)↓
访问数组对应位置

2.2 简单示例

// 假设我们有一个大小为10的数组
String[] table = new String[10];
// 哈希函数:取字符串首字母的ASCII码对10取模
public int hash(String key) {return key.charAt(0) % 10;
}
// 存储键值对
table[hash("apple")] = "苹果";
table[hash("banana")] = "香蕉";
// 查找
String value = table[hash("apple")]; // 快速找到"苹果"

3. 哈希冲突(Hash Collision)

当两个不同的键经过哈希函数计算后得到相同的索引时,就发生了​​哈希冲突​​。这是哈希表设计中的核心问题。

冲突示例:

// "apple" 和 "avocado" 首字母都是'a'
hash("apple")   = 'a' % 10 = 97 % 10 = 7
hash("avocado") = 'a' % 10 = 97 % 10 = 7 ← 冲突!

4. 解决哈希冲突的方法

4.1 链地址法(Separate Chaining)★最常用

​原理​​:每个数组位置不直接存储元素,而是存储一个链表(或其他数据结构),所有哈希到同一位置的元素都放在这个链表中。

// 链地址法的简化实现
class HashMap {class Node {K key;V value;Node next;Node(K key, V value) {this.key = key;this.value = value;}}private Node[] table;private int capacity;// 插入元素public void put(K key, V value) {int index = hash(key) % capacity;Node newNode = new Node<>(key, value);if (table[index] == null) {table[index] = newNode; // 第一个节点} else {// 添加到链表头部newNode.next = table[index];table[index] = newNode;}}// 查找元素public V get(K key) {int index = hash(key) % capacity;Node current = table[index];while (current != null) {if (current.key.equals(key)) {return current.value;}current = current.next;}return null;}
}

​Java的HashMap就是采用链地址法​​,在JDK 1.8之后,当链表长度超过8时,会转换为红黑树以提高性能。

4.2 开放地址法(Open Addressing)

​原理​​:当发生冲突时,按照某种探测序列寻找下一个空闲位置。

4.2.1 线性探测(Linear Probing)
// 线性探测:依次检查下一个位置
public int linearProbe(int index, int attempt, int capacity) {return (index + attempt) % capacity;
}
// 示例:插入键"apple"和"avocado"(哈希值都是7)
// table[7] = "apple"
// table[8] = "avocado"(因为7被占用,检查下一个位置8)
4.2.2 平方探测(Quadratic Probing)
// 平方探测:按平方数跳跃检查
public int quadraticProbe(int index, int attempt, int capacity) {return (index + attempt * attempt) % capacity;
}
4.2.3 双重散列(Double Hashing)
// 使用第二个哈希函数
public int doubleHash(int index, int attempt, int capacity, K key) {int hash2 = secondaryHash(key);return (index + attempt * hash2) % capacity;
}

5. 哈希函数的设计

一个好的哈希函数应该满足:

  1. ​均匀性​​:键应均匀分布在数组中

  2. ​高效性​​:计算速度快

  3. ​确定性​​:相同的键必须产生相同的哈希值

5.1 常见哈希函数设计

// 1. 除法哈希法(最常用)
public int hashByDivision(K key, int capacity) {return key.hashCode() % capacity;
}
// 2. 乘法哈希法
public int hashByMultiplication(K key, int capacity) {double A = 0.6180339887; // 黄金比例的分数部分double hash = key.hashCode() * A;return (int)(capacity * (hash - Math.floor(hash)));
}
// 3. Java String的哈希函数(实际实现)
public int javaStringHash(String s) {int hash = 0;for (int i = 0; i < s.length(); i++) {hash = 31 * hash + s.charAt(i);}return hash;
}

6. Java中的HashMap实现

6.1 HashMap的核心结构

// JDK 1.8+ 的HashMap简化结构
class HashMap {static class Node {final int hash;final K key;V value;Node next;}// 在JDK 1.8中,当链表长度 ≥ 8时转换为红黑树static final class TreeNode extends LinkedHashMap.Entry {TreeNode parent;TreeNode left;TreeNode right;}transient Node[] table; // 哈希桶数组int size;                     // 键值对数量int threshold;                // 扩容阈值(容量 * 负载因子)final float loadFactor;       // 负载因子(默认0.75)
}

6.2 HashMap的重要参数

// 默认初始容量:16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
// 最大容量:2^30
static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认负载因子:0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 树化阈值:链表长度 ≥ 8时转为红黑树
static final int TREEIFY_THRESHOLD = 8;
// 链化阈值:树节点数 ≤ 6时转回链表
static final int UNTREEIFY_THRESHOLD = 6;
// 最小树化容量:64
static final int MIN_TREEIFY_CAPACITY = 64;

6.3 HashMap的扩容机制

// 当 size >= capacity * loadFactor 时触发扩容
HashMap map = new HashMap<>(16, 0.75f);
// 初始阈值 = 16 * 0.75 = 12
// 当插入第13个元素时,触发扩容
// 扩容过程:
// 1. 创建新数组(通常是原容量的2倍)
// 2. 重新计算所有元素的位置(rehash)
// 3. 将元素转移到新数组

7. 时间复杂度分析

操作

平均情况

最坏情况

说明

​插入​

O(1)

O(n)或O(log n)

最坏情况:所有元素哈希到同一位置

​查找​

O(1)

O(n)或O(log n)

链地址法最坏O(n),树化后O(log n)

​删除​

O(1)

O(n)或O(log n)

同查找

​空间​

O(n)

O(n)

存储n个元素

​注意​​:Java HashMap通过树化机制将最坏情况从O(n)优化到O(log n)

8. 哈希表的应用场景

8.1 缓存系统(LRU Cache)

class LRUCache {private HashMap map;private DoublyLinkedList list;private int capacity;// 结合哈希表(快速查找)和双向链表(维护顺序)public V get(K key) {if (!map.containsKey(key)) return null;Node node = map.get(key);moveToHead(node); // 移动到头部表示最近使用return node.value;}
}

8.2 词频统计

public Map wordFrequency(String text) {Map freqMap = new HashMap<>();String[] words = text.split("\\s+");for (String word : words) {freqMap.put(word, freqMap.getOrDefault(word, 0) + 1);}return freqMap;
}
// 使用:统计"hello world hello" → {"hello":2, "world":1}

8.3 快速查找表

// 数据库索引的内存模拟
class UserDatabase {private Map idIndex = new HashMap<>();private Map emailIndex = new HashMap<>();public void addUser(User user) {idIndex.put(user.getId(), user);emailIndex.put(user.getEmail(), user);}// O(1)时间通过ID或邮箱查找用户public User findById(int id) { return idIndex.get(id); }public User findByEmail(String email) { return emailIndex.get(email); }
}

8.4 重复检测

public boolean hasDuplicate(int[] nums) {Set set = new HashSet<>();for (int num : nums) {if (set.contains(num)) return true; // O(1)检测set.add(num);}return false;
}

8.5 两数之和问题

public int[] twoSum(int[] nums, int target) {Map map = new HashMap<>(); // 值->索引的映射for (int i = 0; i < nums.length; i++) {int complement = target - nums[i];if (map.containsKey(complement)) {return new int[]{map.get(complement), i};}map.put(nums[i], i);}return new int[0];
}
// 时间复杂度:O(n),空间复杂度:O(n)

9. 哈希表实现示例

9.1 简单哈希表实现

class SimpleHashMap {private static final int DEFAULT_CAPACITY = 16;private static final double LOAD_FACTOR = 0.75;static class Entry {K key;V value;Entry next;Entry(K key, V value) {this.key = key;this.value = value;}}private Entry[] table;private int size;@SuppressWarnings("unchecked")public SimpleHashMap() {table = new Entry[DEFAULT_CAPACITY];size = 0;}private int hash(K key) {return key == null ? 0 : Math.abs(key.hashCode()) % table.length;}public void put(K key, V value) {if (size >= table.length * LOAD_FACTOR) {resize();}int index = hash(key);Entry newEntry = new Entry<>(key, value);if (table[index] == null) {table[index] = newEntry;} else {Entry current = table[index];while (current.next != null) {if (current.key.equals(key)) {current.value = value; // 更新现有键return;}current = current.next;}current.next = newEntry;}size++;}public V get(K key) {int index = hash(key);Entry current = table[index];while (current != null) {if (current.key.equals(key)) {return current.value;}current = current.next;}return null;}@SuppressWarnings("unchecked")private void resize() {Entry[] oldTable = table;table = new Entry[oldTable.length * 2];size = 0;for (Entry entry : oldTable) {while (entry != null) {put(entry.key, entry.value);entry = entry.next;}}}
}

10. 哈希表相关问题与解决方案

10.1 哈希攻击(Hash Flooding Attack)问题与解决方案

问题描述

哈希攻击是一种​​拒绝服务攻击(DoS)​​,攻击者故意构造大量具有相同哈希值的键,使哈希表性能急剧下降。

攻击原理分析
// 攻击者可以构造大量相同哈希值的键
String[] attackKeys = {"a1", "a2", "a3", ..., "a10000"};
// 被攻击后:所有键都哈希到同一个桶
Map map = new HashMap<>();
for (String key : attackKeys) {map.put(key, "value"); // 时间复杂度从 O(1) 退化为 O(n)
}
解决方案
  • ​使用加密级哈希函数​

  • ​随机化哈希种子​​(主要防御手段)

  • ​树化优化​​(Java HashMap的终极防御)

  • ​请求频率限制​

10.2 内存使用优化问题与解决方案

问题分析

哈希表的内存浪费主要来自数组空间浪费、链表/树节点开销和指针大小等方面。

// 负载因子为0.75时,数组有25%的空间始终空闲
HashMap map = new HashMap<>(16, 0.75f);
// 当包含12个元素时就会触发扩容,数组有4个位置(25%)未被使用
解决方案
  • ​选择合适的负载因子​

  • ​预分配合适容量​

  • ​使用专用数据结构​

  • ​压缩存储技术​

10.3 一致性哈希(Consistent Hashing)问题与解决方案

分布式环境中的问题

传统哈希在节点数量变化时,大部分数据需要重新分布:

// 从3节点扩展到4节点时,大约75%的数据需要重新分布
一致性哈希解决方案
public class ConsistentHash {private final SortedMap circle = new TreeMap<>();// 添加物理节点public void addNode(T node) {// 为每个物理节点创建多个虚拟节点}// 根据键获取对应节点public T getNode(Object key) {// 在环上顺时针查找第一个≥当前哈希值的节点}
}
实际应用场景
  • ​分布式缓存​​(Redis Cluster)

  • ​负载均衡器​

  • ​分布式数据库分片​


哈希表的优势:

  1. ​极快的操作速度​​:平均O(1)的插入、删除、查找

  2. ​灵活性​​:可以存储各种类型的键值对

  3. ​高效内存使用​​:负载因子平衡了时间和空间

哈希表的劣势:

  1. ​无序性​​:元素没有固定顺序(LinkedHashMap可解决)

  2. ​空间开销​​:数组通常不会完全利用

  3. ​哈希冲突​​:设计不良的哈希函数会影响性能

Java中的选择:

  • ​HashMap​​:最常用,非线程安全,允许null键值

  • ​Hashtable​​:线程安全但性能较差,已过时

  • ​ConcurrentHashMap​​:高并发场景的最佳选择

  • ​LinkedHashMap​​:保持插入顺序或访问顺序

  • ​TreeMap​​:按键排序,基于红黑树

​哈希表是现代编程中最重要的数据结构之一​​,几乎所有的编程语言都内置了哈希表实现。理解其原理和特性,对于编写高效程序至关重要!

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

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

相关文章

Github 12.3kstar, 3分钟起步做中后台?Go+Vue 脚手架,把权限、代码生成、RBAC 都封装好了

嗨,我是小华同学,专注解锁高效工作与前沿AI工具!每日精选开源技术、实战技巧,助你省时50%、领先他人一步。👉免费订阅,与10万+技术人共享升级秘籍!go-admin 是一个基于 Gin + Vue 的前后端分离权限管理脚手架,…

电子商务网站建设行情建设网站的网站叫什么男

0 前言 无人驾驶技术是机器学习为主的一门前沿领域&#xff0c;在无人驾驶领域中机器学习的各种算法随处可见&#xff0c;今天学长给大家介绍无人驾驶技术中的车道线检测。 1 车道线检测 在无人驾驶领域每一个任务都是相当复杂&#xff0c;看上去无从下手。那么面对这样极其…

河南城乡建设部网站首页中国十大购物网站排行榜

题目链接 洛谷P4559 题解 只会做\(70\)分的\(O(nlog^2n)\) 如果本来就在区间内的人是不用动的&#xff0c;区间右边的人往区间最右的那些空位跑&#xff0c;区间左边的人往区间最左的那些空位跑 找到这些空位就用二分 主席树 理应可以在主席树上的区间二分而做到\(O(nlogn)\)&…

苏州app制作大型网站的优化方法

网络连通性测试与网络命令验证实验报告西工大计算机网络原理实验报告网络原理实验报告实验名称&#xff1a; 网络连通性测试与网络命令验证 日期&#xff1a; 2015.11&#xff0e;09 班级&#xff1a;学号&#xff1a; 姓名&#xff1a;【一】 预习内容5个常用网络命令&#xf…

北京网站seo哪家公司好wordpress 在线教育主题

存储引擎&#xff1a; 存储引擎是Inndb。它支持行级锁&#xff0c;以及表级锁&#xff0c; 支持事务操作&#xff0c; 需要主键&#xff0c;若未声明主键&#xff0c;则会寻找表中的 not null 以及 unique 修饰的字段。若表中无此类字段&#xff0c;会隐式生成字段。 索引&…

网站需求清单如何用c 做网站

文章目录 一、softmax函数1.1 引入指数形式的优点1.2 引入指数形式的缺点 二、交叉熵损失函数2.1 交叉熵损失函数2.2 softmax与交叉熵损失 参考资料 一、softmax函数 softmax用于多分类过程中&#xff0c;它将多个神经元的输出&#xff0c;映射到&#xff08;0,1&#xff09;区…

250927

目录JT-JY5T1S1-1JT-JY5T1S1-2FT-(JY&VOAs) JT-JY5T1S1-1Good afternoon, Dream Time Travel, How can i help you? Oh, hello, i am interested in the hoilday you offer along the coast near here. Yes, we o…

Ti 毫米波雷达FFT的缩放问题

struct DPU_RangeProcHWA_FFTtuning_t{ /*! @brief Specify amount of right (divide) shift to apply to convert HWA internal 24-bit Range FFT output to 16-bit RadarCube. User should adjust this based on th…

完整教程:多线程——单例模式

完整教程:多线程——单例模式pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco"…

A Twisty Movement

CF933A A Twisty Movement 简化题目 给定一个有 \(1\),\(2\) 两个数字组成的数组中,选择一个子串,将 \(1\) 变成 \(2\),将 \(2\) 变成 \(1\),求出变化后的序列的最长上升子序列。 思路 简单的情况 如果没有变换操…

佛山新网站建设渠道天津工程网站建设

MySQL 中的集群部署方案 前言 这里来聊聊&#xff0c;MySQL 中常用的部署方案。 MySQL Replication MySQL Replication 是官方提供的主从同步方案&#xff0c;用于将一个 MySQL 的实例同步到另一个实例中。Replication 为保证数据安全做了重要的保证&#xff0c;是目前运用…

山东网站建设设计小清新个人网站

七、高并发内存池–Page Cache 7.1 PageCache的工作原理 PageCache是以span的大小(以页为单位)和下标一一对应为映射关系的哈希桶&#xff0c;下标是几就说明这个哈希桶下挂的span的大小就是几页的&#xff0c;是绝对映射的关系。因为PageCache也是全局只有唯一一个的&#x…

完整教程:iOS 混淆与反调试反 Hook 实战,运行时防护、注入检测与安全加固流程

完整教程:iOS 混淆与反调试反 Hook 实战,运行时防护、注入检测与安全加固流程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-f…

3.WPF - 依赖属性 - 实践

3.WPF - 依赖属性 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &q…

Attention进阶史(MHA, MQA, GQA, MLA)

在深度学习领域,注意力机制(Attention Mechanism)自诞生以来便成为推动自然语言处理和计算机视觉等任务发展的核心动力。从最初的多头注意力(MHA)到如今的高效变体,如多查询注意力(MQA)、分组查询注意力(GQA)…

实用指南:AI编程时代的变革:Replit CEO对话深度解析

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025北京个性旅行自由行口碑推荐北京汇通清源国际旅游公司,满足独特需求,自由随心

2025年,想要在北京开启一场个性十足的自由行?那就一定要了解一下北京汇通清源国际旅游公司。这家成立于2014年的旅游公司,注册资本300万元人民币,坐落于北京市朝阳区,业务广泛,涵盖全北京各个区域的旅游业务,包…

广州专业做网站多少钱加速游戏流畅的软件

今天我来讲一下在Linux下各环境的搭建&#xff0c;主要就讲一下jdk、MySQL、和一个代理服务器nginx 1、 jdk的安装配置 1)卸载自带openjdk 当我们拿到一个全新的ECS的时候上面有的会自带一个openjdk&#xff0c;但是我们一般不会用这个&#xff0c;所以在这里我们会先卸载这个自…

电子商务网站开发文档在线qq登录无需下载

选择、插入、冒泡三种算是最典型的排序算法了&#xff0c;空间复杂度都为O(1) 选择排序时间复杂度跟初始数据顺序无关&#xff0c;O(n2)&#xff0c;而且还不稳定; 插入排序时间复杂度跟初始数据顺序有关最好O(n),最坏O(n2)&#xff0c;稳定 冒泡排序时间复杂度跟初始数据顺序有…

网站 自定义表单招聘网站开发背景

陆游的《诗人苦学说》&#xff1a;从藻绘到“功夫在诗外” 今天看万维钢的《万万没想到》一书&#xff0c;看到陆游的功夫在诗外的句子&#xff0c;特意去查找这首诗的原文。故而有此文。 我国学人还往往过分强调“功夫在诗外”这句陆游的名言&#xff0c;认为提升综合素质是一…