Logo
Section 1 / 14

哈希

了解哈希是什么以及它是如何工作的。

导入

哈希

了解哈希是什么以及它是如何工作的。

什么是哈希?

哈希数据结构有多种名称:哈希、哈希表、映射、字典,甚至在 JavaScript 中也被称为对象。哈希和数组是每个程序员都必须掌握的基础数据结构。它们虽然不同,但设计目标相同:用于存储和检索数据。两者唯一的区别在于实现存储与检索操作的方式。

那么什么是数组?我们如何向数组中存储数据并从中取回数据呢?想象一个孩子有很多玩具。妈妈把这些玩具都放在一个架子上,并给每个玩具分配了一个连续的编号:1、2、3……等等。如果我们想找到最喜欢的玩具,就必须从头到尾逐一查看整个架子上的玩具。如果架子很长,这个过程可能需要花费不少时间。然而,如果我们知道某个玩具的编号,就能立刻找到它。

哈希看起来像一个神奇的桶。它没有顺序,我们也不清楚它的内部机制。我们可以把任何对象放进这个神奇的桶里,并给它起个名字:“嘿,神奇的桶,这里有一个球。” 我们也可以随时从中取出某个对象:“嘿,神奇的桶,把叫‘球’的那个东西给我。” 关键点在于,我们通过命名来存取对象,这个名称就是 键(或哈希键)。查找元素的时间复杂度为常数时间,即瞬间完成。

工作原理

但这个神奇的桶究竟是如何工作的?为什么在数组中我们需要扫描整个架子,而哈希却能瞬间完成查找?秘密在于它的内部组织方式以及底层工作机制:哈希内部由多个小桶组成——“桶”才是其真实结构名称。对象会根据自身的特征(如颜色)被分配到特定的小桶中。随着对象数量增加,需要的内部小桶也会相应增多。

既然哈希这么优秀,为什么不能一直使用它呢?

首先,这种数据结构不保证任何顺序。当我们用 push 向数组添加数据时,可以明确知道哪个元素是第一个加入的,哪个是后来加入的。但在哈希中,一旦数据插入后,就不再保留插入顺序,因此无法判断某个值是什么时候被放入的。

注意:尽管哈希本身不保证顺序,但 Ruby 的哈希实现确实保证了插入顺序,不过我们并不建议依赖这一点。官方文档 中提到:

“哈希按对应键的插入顺序枚举其值。”

但由于每位网页开发者至少应具备基础的 JavaScript 知识,我们来看看 JavaScript 官方文档怎么说:

“对象属于 Object 类型,是一个无序的属性集合,包含原始值、对象或函数。”

不过,在较新的 JavaScript 版本(ES6 及以上)中,Map 类(一种哈希实现)能够按照插入顺序返回值。因此,通用原则是:不要依赖哈希的顺序。

其次,每种数据结构都有其“最坏情况下的执行时间”。对于哈希来说,最坏情况是线性时间 O(N),意味着代码需要遍历所有元素。而数组通过索引查找始终是常数时间 O(1)。因此,如果你愿意使用类似 `0, 1, 2` 这样的索引,而不是 `:red`, `:green`, `:blue` 这样的键,那么使用数组更优,因为它性能更有保障。

不过,在实际应用中,最坏情况并不常见。程序员选择哈希的主要原因在于可读性:说“取球”比说“取索引为 148 的对象”要直观得多。

Figuring out
Figuring out

数组

在 Ruby 中定义哈希