小编典典

JavaScript hashmap 等价物

all

此表示法:

var hash = {};
hash[X]

实际上并没有散列对象X;它实际上只是转换X为一个字符串(.toString()如果它是一个对象,或者是各种原始类型的其他一些内置转换),然后在“
hash”中查找该字符串,而不对其进行散列。也不会检查对象相等性 - 如果两个不同的对象具有相同的字符串转换,它们只会相互覆盖。

鉴于此 - JavaScript 中是否有任何有效的 hashmap 实现?


阅读 117

收藏
2022-03-23

共1个答案

小编典典

自己手动散列您的对象,并将生成的字符串用作常规 JavaScript 字典的键。毕竟,您最有能力知道是什么让您的对象与众不同。我就是做这个的。

例子:

var key = function(obj){
  // Some unique object-dependent key
  return obj.totallyUniqueEmployeeIdKey; // Just an example
};

var dict = {};

dict[key(obj1)] = obj1;
dict[key(obj2)] = obj2;

通过这种方式,您可以控制由 JavaScript 完成的索引,而无需繁重的内存分配和溢出处理。

当然,如果你真的想要“工业级的解决方案”,你可以构建一个key函数参数化的类,并拥有容器所需的所有API,但是——我们使用JavaScript,并试图简单轻量级,所以这个功能解决方案简单快捷。

密钥功能可以像选择对象的正确属性一样简单,例如,一个密钥或一组密钥,它们已经是唯一的,密钥的组合,它们一起是唯一的,或者像使用一些密码散列一样复杂,例如在DojoX
编码
DojoX
UUID
中。虽然后一种解决方案可能会产生唯一的密钥,但我个人会不惜一切代价避免它们,特别是如果我知道是什么让我的对象独一无二。

2014 年更新: 2008 年回答这个简单的解决方案仍然需要更多解释。让我以问答形式澄清这个想法。

您的解决方案没有真正的哈希。 它在哪里???

JavaScript
是一种高级语言。它的基本原语(Object)包括一个哈希表来保存属性。为了提高效率,这个哈希表通常是用低级语言编写的。使用带有字符串键的简单对象,我们无需任何努力即可使用高效实现的哈希表。

你怎么知道他们使用哈希?

有三种主要的方法来保持对象集合可通过键寻址:

  • 无序。在这种情况下,要通过其键检索对象,我们必须遍历所有在找到它时停止的键。平均而言,它将进行 n/2 次比较。
  • 订购。
    • 示例#1:一个排序数组——进行二分搜索,我们将在平均 ~log2(n) 次比较之后找到我们的键。好多了。
    • 示例#2:一棵树。再次是 ~log(n) 尝试。
  • 哈希表。平均而言,它需要一个恒定的时间。比较:O(n) vs. O(log n) vs. O(1)。繁荣。

显然,JavaScript 对象以某种形式使用哈希表来处理一般情况。

浏览器供应商真的使用哈希表吗???

真的。

他们处理碰撞吗?

是的。往上看。如果您在不相等的字符串上发现冲突,请立即向供应商提交错误。

那么你的想法是什么?

如果你想散列一个对象,找出是什么使它独一无二并将其用作键。不要尝试计算真正的哈希或模拟哈希表——它已经被底层 JavaScript 对象有效地处理了。

将此键与 JavaScript 一起使用Object以利用其内置哈希表,同时避免与默认属性可能发生的冲突。

帮助您入门的示例:

  • 如果您的对象包含唯一的用户名 - 将其用作键。
  • 如果它包含一个唯一的客户编号 - 将其用作密钥。
    • 如果它包含唯一的政府颁发的号码,如美国SSN或护照号码,并且您的系统不允许重复 - 将其用作密钥。
  • 如果字段组合是唯一的 - 将其用作键。
    • 美国州缩写+驾驶执照号码是一个很好的关键。
    • 国家缩写+护照号码也是一个很好的关键。
  • 字段或整个对象上的某些函数可以返回唯一值——将其用作键。

我使用了您的建议并使用用户名缓存了所有对象。 但是一些聪明的人被命名为“toString”,这是一个内置属性!我现在该怎么办?

显然,如果生成的密钥完全由拉丁字符组成的可能性很小,那么您应该对此采取一些措施。例如,在开头或结尾添加您喜欢的任何非拉丁 Unicode
字符以与默认属性取消冲突:“#toString”、“#MarySmith”。如果使用复合键,则使用某种非拉丁分隔符分隔键组件:“name,city,state”。

一般来说,这是我们必须发挥创造力并选择具有给定限制(唯一性,与默认属性的潜在冲突)的最简单键的地方。

注意:根据定义,唯一键不会发生冲突,而潜在的哈希冲突将由底层Object.

你为什么不喜欢工业解决方案?

恕我直言,最好的代码根本就是没有代码:它没有错误,不需要维护,易于理解,并且可以立即执行。我看到的所有“JavaScript 中的哈希表”都超过 100
行代码,并且涉及多个对象。将其与:dict[key] = value.

另一点:使用 JavaScript 和相同的原始对象来实现已经实现的东西,甚至有可能击败用低级语言编写的原始对象的性能吗?

我仍然想在没有任何键的情况下散列我的对象!

我们很幸运:ECMAScript
6
(2015
年 6 月发布)定义了mapset

从定义来看,他们可以使用对象的地址作为键,这使得对象在没有人工键的情况下立即区分。OTOH,两个不同但相同的对象,将被映射为不同的。

来自MDN的比较细分:

对象与 Maps
类似,都允许您将键设置为值、检索这些值、删除键以及检测是否在键中存储了某些内容。正因为如此(并且因为没有内置的替代品),对象在历史上一直被用作地图;但是,在某些情况下,使用
Map 更可取的重要区别是:

  • 对象的键是字符串和符号,而它们可以是 Map 的任何值,包括函数、对象和任何原语。
  • Map 中的键是有序的,而添加到对象的键不是。因此,在对其进行迭代时, Map 对象会按插入顺序返回键。
  • 您可以使用 size 属性轻松获取 Map 的大小,而 Object 中的属性数量必须手动确定。
  • Map 是可迭代的,因此可以直接迭代,而对 Object 进行迭代则需要以某种方式获取其键并对其进行迭代。
  • 一个对象有一个原型,所以如果你不小心,地图中有默认键可能会与你的键发生冲突。从 ES5 开始,这可以通过使用 map =
    Object.create(null) 绕过,但很少这样做。
  • 在涉及频繁添加和删除密钥对的场景中,Map 可能会表现得更好。
2022-03-23