且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

JavaScript hashmap 等价物

更新时间:2021-12-05 15:30:35

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

Hash your objects yourself manually, and use the resulting strings as keys for a regular JavaScript dictionary. After all, you are in the best position to know what makes your objects unique. That's what I do.

示例:

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 完成的索引,而无需繁重的内存分配和溢出处理.

This way you can control indexing done by JavaScript without heavy lifting of memory allocation, and overflow handling.

当然,如果你真的想要工业级解决方案",你可以构建一个由 key 函数参数化的类,并使用容器的所有必要 API,但是……我们使用 JavaScript,并试图成为简单轻便,所以这个功能解决方案简单快速.

Of course, if you truly want the "industrial-grade solution", you can build a class parameterized by the key function, and with all the necessary API of the container, but … we use JavaScript, and trying to be simple and lightweight, so this functional solution is simple and fast.

键功能可以像选择对象的正确属性一样简单,例如,一个键或一组键,它们已经是唯一的,键的组合,它们一起是唯一的,或者像使用一些键一样复杂DojoX encodingDojoX UUID.虽然后一种解决方案可能会产生唯一的键,但我个人会不惜一切代价避免使用它们,尤其是在我知道是什么让我的对象独一无二的时候.

The key function can be as simple as selecting right attributes of the object, e.g., a key, or a set of keys, which are already unique, a combination of keys, which are unique together, or as complex as using some cryptographic hashes like in DojoX encoding, or DojoX UUID. While the latter solutions may produce unique keys, personally I try to avoid them at all costs, especially, if I know what makes my objects unique.

2014 年更新: 早在 2008 年就回答了这个简单的解决方案,但仍然需要更多解释.让我以问答形式阐明这个想法.

Update in 2014: Answered back in 2008 this simple solution still requires more explanations. Let me clarify the idea in a Q&A form.

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

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

JavaScript is a high-level language. Its basic primitive (Object) includes a hash table to keep properties. This hash table is usually written in a low-level language for efficiency. Using a simple object with string keys we use an efficiently implemented hash table without any efforts on our part.

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

有三种主要方法可以使对象集合可以通过键寻址:

There are three major ways to keep a collection of objects addressable by a key:

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

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

    Obviously JavaScript objects use hash tables in some form to handle general cases.

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

    真的.

    • Chrome/node.js/V8: JSObject. Look for NameDictionary and NameDictionaryShape with pertinent details in objects.cc and objects-inl.h.
    • Firefox/Gecko: JSObject, NativeObject, and PlainObject with pertinent details in jsobj.cpp and vm/NativeObject.cpp.

    他们会处理碰撞吗?

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

    Yes. See above. If you found a collision on unequal strings, please do not hesitate to file a bug with a vendor.

    那么你的想法是什么?

    如果您想对一个对象进行哈希处理,请找出它的独特之处并将其用作键.不要尝试计算真正的哈希或模拟哈希表——它已经被底层 JavaScript 对象有效地处理了.

    If you want to hash an object, find what makes it unique and use it as a key. Do not try to calculate a real hash or emulate hash tables — it is already efficiently handled by the underlying JavaScript object.

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

    Use this key with JavaScript's Object to leverage its built-in hash table while steering clear of possible ***es with default properties.

    帮助您入门的示例:

    • 如果您的对象包含唯一的用户名 - 将其用作键.
    • 如果它包含唯一的客户编号 - 将其用作键.
      • 如果它包含唯一的***颁发的号码,例如美国 SSN,或护照号码,以及您的系统不允许重复 - 将其用作键.
      • If your objects include a unique user name — use it as a key.
      • If it includes a unique customer number — use it as a key.
        • If it includes unique government-issued numbers like US SSNs, or a passport number, and your system doesn't allow duplicates — use it as a key.
        • 美国州的缩写 + 驾照号码是很好的钥匙.
        • 国家缩写 + 护照号码也是一个很好的关键.

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

        显然,如果生成的密钥完全由拉丁字符组成,那么您应该采取一些措施.例如,在开头或结尾添加您喜欢的任何非拉丁 Unicode 字符以与默认属性取消冲突:#toString"、#MarySmith".如果使用复合键,请使用某种非拉丁分隔符分隔键组件:名称、城市、州".

        Obviously, if it is even remotely possible that the resulting key will exclusively consists of Latin characters, you should do something about it. For example, add any non-Latin Unicode character you like at the beginning or at the end to un-*** with default properties: "#toString", "#MarySmith". If a composite key is used, separate key components using some kind of non-Latin delimiter: "name,city,state".

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

        In general, this is the place where we have to be creative and select the easiest keys with given limitations (uniqueness, potential ***es with default properties).

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

        Note: unique keys do not *** by definition, while potential hash ***es will be handled by the underlying Object.

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

        恕我直言,***的代码就是完全没有代码:它没有错误、不需要维护、易于理解并且可以立即执行.所有JavaScript 中的哈希表"我看到超过 100 行代码,涉及多个对象.比较它:dict[key] = value.

        IMHO, the best code is no code at all: it has no errors, requires no maintenance, easy to understand, and executes instantaneously. All "hash tables in JavaScript" I saw were >100 lines of code, and involved multiple objects. Compare it with: dict[key] = value.

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

        Another point: is it even possible to beat a performance of a primordial object written in a low-level language, using JavaScript and the very same primordial objects to implement what is already implemented?

        我仍然想在没有任何键的情况下对我的对象进行哈希处理!

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

        We are in luck: ECMAScript 6 (released in June 2015) defines map and set.

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

        Judging by the definition, they can use an object's address as a key, which makes objects instantly distinct without artificial keys. OTOH, two different, yet identical objects, will be mapped as distinct.

        MDN 的比较细分:

        Objects 与 Maps 类似,都可以让您将键设置为值,检索这些值,删除键,并检测是否存在存储在一个键上.因此(并且因为没有内置替代方案),对象在历史上一直被用作地图;然而,有一些重要的区别使得使用 Map 更可取某些情况:

        Objects are similar to Maps in that both let you set keys to values, retrieve those values, delete keys, and detect whether something is stored at a key. Because of this (and because there were no built-in alternatives), Objects have been used as Maps historically; however, there are important differences that make using a Map preferable in certain cases:

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