- 内容提要
- 序 1:程序里的世界
- 序 2:最后一层表象
- 关于本书
- 致谢
- 引言:简单的本源
- 篇一:计算系统
- 第 1 章 数,以及对数据的性质的思考
- 第 2 章 逻辑
- 第 3 章 抽象
- 篇二:语言及其面临的系统
- 第 4 章 语言
- 第 5 章 从功能到系统
- 篇三:程序设计的核心思想
- 第 6 章 数据结构:顺序存储
- 第 7 章 数据结构:散列存储
- 第 8 章 执行体与它在执行过程中的环境
- 第 9 章 语法树及其执行过程
- 第 10 章 对象系统:表达、使用与模式
- 篇四:应用开发基础
- 第 11 章 应用开发的背景与成因
- 第 12 章 应用开发技术
- 第 13 章 开发视角下的工程问题
- 第 14 章 应用程序设计语言的复杂性
- 篇五:系统的基础部件
- 第 15 章 分布
- 第 16 章 依赖
- 第 17 章 消息
- 第 18 章 系统
- 篇六:系统的基本组织方法与原理
- 第 19 章 行为的组织及其抽象
- 第 20 章 领域间的组织
- 附一:主要编程范式 及其语言特性关系
- 附二:继承与混合,略谈系统的构建方式
- 附三:像大师们一样思考——从 UML 何时死掉 谈起
- 附四:VCL 已死,RAD 已死
7.4 Key:对名字不可或缺的验证
显然,(图 12 右侧抽象概念中的)“键/值”作为抽象系统,的确可以让我们通过标识找到值。我们甚至可以为 索引数组在这个系统上的抽象 定义一个函数——换言之,索引数组也就是这个系统的一个特例:
1 2 3 4 |
|
但是我们也发现正因为索引数组是自映射的,所以它的映射关系是一对一的(传入 i
,返回 i
),而此前的 hash_sum_16()
却是多对一的,例如 'OrderNum'
映射为 12,但如果传入标识 'Order'
,其映射的结果也会是 12。
因此我们面临要在同一个位置上放两个或者更多数据的情况。这就是 哈希冲突(哈希碰撞,hash collision) 。对此我们可以增大哈希表的长度,但即使它超过可能的哈希键个数,(在不同的算法下)仍然存在冲突的可能。既然我们承认冲突必然存在,因此在找到 Value 之后再做一次验证就是必需的了。这里的验证也有许多种做法,其中之一是用某种新算法再做一次 Hash()
。尽管两个不同的 Key 在两种不同的 Hash()
之后仍然相同的机率相当低,但是——仍如此前所述的——还是必然会存在。所以最终的一个步骤通常还是对 Key 的直接识别,例如字符串的逐字符比较,或者数据存储区块的逐字节比较,或者基于顺序存储的、区块地址的比较。这种情况下,数据区必须保留原始的 Name/Key 值,如图 13 所示。
图 13 验证:保留 Name/Key 的原因
有两种特殊情况,其一是元素在系统中不一定完全出现,但其可能值是预知的,例如一周的七天;其二是元素总是会完全出现在系统中,例如键盘上的所有键。这两种情况所面临的数据集都是有限的,对此我们仍有机会设计一个函数来使得每一个 Key 所计算的 Code 都保持唯一。这样的哈希表是静态大小的,其长度为数据集范围的上限,即:
Get_Length(Hash_Buckets) == Get_Element_Count(Data_Set)
这其实是为每个元素创建了唯一的哈希码映射,只需要比对哈希码值即可确认数据,也因此就无须再保留原始的 Name/Key 值了。这同时也节省了图 13 中最后一步比对 Name/Key 的开销。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论