JavaScript Hashmap Equivalent
如本答案更新3中所述,该符号:
1 2 | var hash = {}; hash[X] |
实际上并不散列对象
考虑到这一点,在javascript中有没有有效的哈希图实现?(例如,
为什么不自己手动散列对象,并使用生成的字符串作为常规JavaScript字典的键呢?毕竟,你处于了解什么使你的对象独一无二的最佳位置。我就是这么做的。好的。
例子:好的。
1 2 3 4 5 6 7 8 9 | 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完成的索引,而无需大幅提升内存分配和溢出处理。好的。
当然,如果您真的想要"工业级解决方案",您可以构建一个由键函数参数化的类,并使用容器的所有必要API,但是…我们使用javascript,并尝试简单和轻量,因此此功能解决方案简单快速。好的。
key函数可以简单到选择对象的正确属性,例如,一个键或一组已经唯一的键,键的组合,它们在一起是唯一的,或者像使用Dojox编码或Dojox Uuid中的一些加密散列一样复杂。虽然后一种解决方案可能会生成唯一的密钥,但我个人会不惜一切代价避免使用它们,特别是如果我知道是什么使我的对象独一无二的话。好的。
2014年的更新:在2008年的回复中,这个简单的解决方案仍然需要更多的解释。让我用问答的形式来澄清这个想法。好的。
您的解决方案没有真正的哈希值。它在哪里????好的。
javascript是一种高级语言。它的基本本原(对象)包含哈希表以保留属性。这个哈希表通常是写的为了提高效率而使用低级语言。使用一个带有字符串键的简单对象,我们可以使用一个有效实现的哈希表,而不需要付出任何努力。好的。
你怎么知道他们用哈希?好的。
有三种主要方法可以保持对象集合可通过键寻址:好的。
- 无序的在本例中,为了通过一个对象的键来检索它,我们必须遍历所有在找到它时停止的键。平均而言,这需要n/2的比较。
- 命令。
- 示例1:排序数组—执行二进制搜索时,我们将在平均~log2(n)个比较后找到密钥。好多了。
- 示例2:树。再次尝试~log(n)次。
- 哈希表。平均来说,它需要一个恒定的时间。比较:O(n)与O(logn)与O(1)。繁荣。
显然,JavaScript对象使用某种形式的哈希表来处理一般情况。好的。
浏览器供应商真的使用哈希表吗????好的。
真的?好的。
- chrome/node.js/v8版本:jsObjt。寻找名称字典和名称字典形状objects.cc中的相关细节和对象。
- 火狐/壁虎:JSObjectNativeObject,和带有相关细节的普通对象jsObj.CPP与vm/native对象.cpp。
他们处理碰撞吗?好的。
对。见上文。如果在不相等的字符串上发现冲突,请立即向供应商提交错误。好的。
你的想法是什么?好的。
如果你想散列一个对象,找到它的唯一性并把它作为一个键。不要试图计算真正的哈希或模拟哈希表,它已经被底层的javascript对象有效地处理了。好的。
将此键与javascript
启动示例:好的。
- 如果对象包含唯一的用户名,请将其用作键。
- 如果包含唯一的客户编号,请将其用作密钥。
- 如果它包含唯一的政府颁发的号码,如SSN或护照号码,并且您的系统不允许重复,请将其用作密钥。
- 如果字段组合是唯一的,请将其用作键。
- 州缩写+驾驶证号码是一把很好的钥匙。
- 国家缩写+护照号码也是一把很好的钥匙。
- 字段或整个对象上的某些函数可以返回一个唯一的值—将其用作键。
我使用了您的建议,并使用用户名缓存了所有对象。但有一个聪明人叫"ToString",这是一个内置的财产!我现在该怎么办?好的。
显然,如果最终得到的密钥完全由拉丁字符组成的可能性很小,那么您应该做些什么。例如,在开头或结尾添加您喜欢的任何非拉丁Unicode字符,以取消与默认属性的冲突:"ToString"、"Marysmith"。如果使用组合键,请使用某种非拉丁分隔符分隔键组件:"名称、城市、州"。好的。
一般来说,这是我们必须具有创造性的地方,并选择具有给定限制(唯一性、可能与默认属性冲突)的最简单键。好的。
注意:唯一键不按定义冲突,而潜在的哈希冲突将由基础
你为什么不喜欢工业解决方案?好的。
imho,最好的代码根本不是代码:它没有错误,不需要维护,易于理解,并且可以即时执行。我看到的所有"javascript中的哈希表"都是>100行代码,涉及多个对象。与:
另一点:是否有可能击败用低级语言编写的原始对象的性能,使用JavaScript和非常相同的原始对象来实现已经实现的内容?好的。
我仍然想散列没有任何键的对象!好的。
我们很幸运:EcmaScript 6(定于2015年年中发布,之后1-2年内发布或发布)定义了地图和集合。好的。
从定义上看,它们可以使用对象的地址作为一个键,这使得对象在没有人工键的情况下可以立即区分。Otoh,两个不同但相同的对象,将被映射为不同的。好的。
MDN的比较分解:好的。
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:Ok.
- The keys of an Object are Strings and Symbols, whereas they can be any value for a Map, including functions, objects, and any primitive.
- The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of
insertion.- You can get the size of a Map easily with the size property, while the number of properties in an Object must be determined manually.
- A Map is an iterable and can thus be directly iterated, whereas iterating over an Object requires obtaining its keys in some fashion
and iterating over them.- An Object has a prototype, so there are default keys in the map that could collide with your keys if you're not careful. As of ES5 this can
be bypassed by using map = Object.create(null), but this is seldom
done.- A Map may perform better in scenarios involving frequent addition and removal of key pairs.
好啊。
问题描述
JavaScript没有内置的通用映射类型(有时称为关联数组或字典),它允许通过任意键访问任意值。Javascript的基本数据结构是对象,这是一种特殊类型的映射,只接受字符串作为键,并具有特殊的语义,如原型继承、getter和setter以及其他一些voodoo。
当使用对象作为映射时,您必须记住,键将通过
javascript的内置数组类型一点也没有帮助:javascript数组不是关联数组,而是具有一些更特殊属性的对象。如果你想知道它们为什么不能用作地图,请看这里。
尤金的解决方案EugeneLazutkin已经描述了使用自定义哈希函数生成唯一字符串的基本思想,该字符串可用于将关联值作为字典对象的属性进行查找。这很可能是最快的解决方案,因为对象在内部实现为哈希表。
- 注意:散列表(有时称为散列映射)是使用支持数组和通过数字散列值进行查找的映射概念的特殊实现。运行时环境可能会使用其他结构(如搜索树或跳过列表)来实现JavaScript对象,但由于对象是基本的数据结构,因此应充分优化它们。
为了获得任意对象的唯一哈希值,一种可能是使用全局计数器并将哈希值缓存在对象本身(例如名为
这样做的哈希函数既适用于原语值也适用于对象是:
1 2 3 4 5 6 7 | function hash(value) { return (typeof value) + ' ' + (value instanceof Object ? (value.__hash || (value.__hash = ++arguments.callee.current)) : value.toString()); } hash.current = 0; |
这个功能可以按照尤金的描述使用。为了方便起见,我们将把它进一步包装在一个
下面的实现还将把键值对存储在一个双链接列表中,以便在键值和值之间进行快速迭代。要提供自己的哈希函数,可以在创建后覆盖实例的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | // linking the key-value-pairs is optional // if no argument is provided, linkItems === undefined, i.e. !== false // --> linking will be enabled function Map(linkItems) { this.current = undefined; this.size = 0; if(linkItems === false) this.disableLinking(); } Map.noop = function() { return this; }; Map.illegal = function() { throw new Error("illegal operation for maps without linking"); }; // map initialisation from existing object // doesn't add inherited properties if not explicitly instructed to: // omitting foreignKeys means foreignKeys === undefined, i.e. == false // --> inherited properties won't be added Map.from = function(obj, foreignKeys) { var map = new Map; for(var prop in obj) { if(foreignKeys || obj.hasOwnProperty(prop)) map.put(prop, obj[prop]); } return map; }; Map.prototype.disableLinking = function() { this.link = Map.noop; this.unlink = Map.noop; this.disableLinking = Map.noop; this.next = Map.illegal; this.key = Map.illegal; this.value = Map.illegal; this.removeAll = Map.illegal; return this; }; // overwrite in Map instance if necessary Map.prototype.hash = function(value) { return (typeof value) + ' ' + (value instanceof Object ? (value.__hash || (value.__hash = ++arguments.callee.current)) : value.toString()); }; Map.prototype.hash.current = 0; // --- mapping functions Map.prototype.get = function(key) { var item = this[this.hash(key)]; return item === undefined ? undefined : item.value; }; Map.prototype.put = function(key, value) { var hash = this.hash(key); if(this[hash] === undefined) { var item = { key : key, value : value }; this[hash] = item; this.link(item); ++this.size; } else this[hash].value = value; return this; }; Map.prototype.remove = function(key) { var hash = this.hash(key); var item = this[hash]; if(item !== undefined) { --this.size; this.unlink(item); delete this[hash]; } return this; }; // only works if linked Map.prototype.removeAll = function() { while(this.size) this.remove(this.key()); return this; }; // --- linked list helper functions Map.prototype.link = function(item) { if(this.size == 0) { item.prev = item; item.next = item; this.current = item; } else { item.prev = this.current.prev; item.prev.next = item; item.next = this.current; this.current.prev = item; } }; Map.prototype.unlink = function(item) { if(this.size == 0) this.current = undefined; else { item.prev.next = item.next; item.next.prev = item.prev; if(item === this.current) this.current = item.next; } }; // --- iterator functions - only work if map is linked Map.prototype.next = function() { this.current = this.current.next; }; Map.prototype.key = function() { return this.current.key; }; Map.prototype.value = function() { return this.current.value; }; |
例子
下面的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 | var map = new Map; map.put('spam', 'eggs'). put('foo', 'bar'). put('foo', 'baz'). put({}, 'an object'). put({}, 'another object'). put(5, 'five'). put(5, 'five again'). put('5', 'another five'); for(var i = 0; i++ < map.size; map.next()) document.writeln(map.hash(map.key()) + ' : ' + map.value()); |
生成此输出:
1 2 3 4 5 6 | string spam : eggs string foo : baz object 1 : an object object 2 : another object number 5 : five again string 5 : another five |
进一步考虑
Pez建议用hash函数覆盖
编辑:我的
我知道这个问题很古老,但是现在有一些非常好的解决办法可以通过外部图书馆来解决。
- JavaS
- 不变的
javascript也提供了它的语言
- 地图
这里是一种使用Java地图类似的简单方便的方法:
1 2 3 4 5 6 | var map= { 'map_name_1': map_value_1, 'map_name_2': map_value_2, 'map_name_3': map_value_3, 'map_name_4': map_value_4 } |
为了得到这个值:
1 2 3 | alert( map['map_name_1'] ); // fives the value of map_value_1 ...... etc ..... |
您可以使用es6
-
WeakMaps are key/value maps in which keys are objects.
-
Map objects are simple key/value maps. Any value (both objects and primitive values) may be used as either a key or a value.
请注意,两者都不受广泛支持,但您可以使用ES6垫片(需要本机ES5或ES5垫片)来支持
根据EcmaScript 2015(ES6)标准,javascript有一个映射实现。这里可以找到更多关于
基本用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var myMap = new Map(); var keyString ="a string", keyObj = {}, keyFunc = function () {}; // setting the values myMap.set(keyString,"value associated with 'a string'"); myMap.set(keyObj,"value associated with keyObj"); myMap.set(keyFunc,"value associated with keyFunc"); myMap.size; // 3 // getting the values myMap.get(keyString); //"value associated with 'a string'" myMap.get(keyObj); //"value associated with keyObj" myMap.get(keyFunc); //"value associated with keyFunc" |
您必须存储在对象/值对的一些内部状态耦合中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | HashMap = function(){ this._dict = []; } HashMap.prototype._get = function(key){ for(var i=0, couplet; couplet = this._dict[i]; i++){ if(couplet[0] === key){ return couplet; } } } HashMap.prototype.put = function(key, value){ var couplet = this._get(key); if(couplet){ couplet[1] = value; }else{ this._dict.push([key, value]); } return this; // for chaining } HashMap.prototype.get = function(key){ var couplet = this._get(key); if(couplet){ return couplet[1]; } } |
并以此方式使用:
1 2 3 4 5 6 | var color = {}; // unique object instance var shape = {}; // unique object instance var map = new HashMap(); map.put(color,"blue"); map.put(shape,"round"); console.log("Item is", map.get(color),"and", map.get(shape)); |
当然,这个实现也是沿着O(N)的路线进行的。Eugene上面的例子是获得哈希的唯一方法,它可以以任何您期望的速度从真正的哈希中获得。
更新:
根据尤金的回答,另一种方法是在所有对象上附加一个唯一的ID。我最喜欢的方法之一是采用从对象超类继承的内置方法之一,将其替换为自定义函数传递并将属性附加到该函数对象。如果您要重写我的hashmap方法来执行此操作,它将如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | HashMap = function(){ this._dict = {}; } HashMap.prototype._shared = {id: 1}; HashMap.prototype.put = function put(key, value){ if(typeof key =="object"){ if(!key.hasOwnProperty._id){ key.hasOwnProperty = function(key){ return Object.prototype.hasOwnProperty.call(this, key); } key.hasOwnProperty._id = this._shared.id++; } this._dict[key.hasOwnProperty._id] = value; }else{ this._dict[key] = value; } return this; // for chaining } HashMap.prototype.get = function get(key){ if(typeof key =="object"){ return this._dict[key.hasOwnProperty._id]; } return this._dict[key]; } |
这个版本似乎只是稍微快一点,但理论上,对于大型数据集来说,它会快得多。
不幸的是,上述答案都不适合我的情况:不同的键对象可能有相同的哈希代码。因此,我编写了一个简单的Java类HashMap版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | function HashMap() { this.buckets = {}; } HashMap.prototype.put = function(key, value) { var hashCode = key.hashCode(); var bucket = this.buckets[hashCode]; if (!bucket) { bucket = new Array(); this.buckets[hashCode] = bucket; } for (var i = 0; i < bucket.length; ++i) { if (bucket[i].key.equals(key)) { bucket[i].value = value; return; } } bucket.push({ key: key, value: value }); } HashMap.prototype.get = function(key) { var hashCode = key.hashCode(); var bucket = this.buckets[hashCode]; if (!bucket) { return null; } for (var i = 0; i < bucket.length; ++i) { if (bucket[i].key.equals(key)) { return bucket[i].value; } } } HashMap.prototype.keys = function() { var keys = new Array(); for (var hashKey in this.buckets) { var bucket = this.buckets[hashKey]; for (var i = 0; i < bucket.length; ++i) { keys.push(bucket[i].key); } } return keys; } HashMap.prototype.values = function() { var values = new Array(); for (var hashKey in this.buckets) { var bucket = this.buckets[hashKey]; for (var i = 0; i < bucket.length; ++i) { values.push(bucket[i].value); } } return values; } |
注意:键对象必须"实现"hashcode()和equals()方法。
在ECMA6中,可以使用weakmap
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | var wm1 = new WeakMap(), wm2 = new WeakMap(), wm3 = new WeakMap(); var o1 = {}, o2 = function(){}, o3 = window; wm1.set(o1, 37); wm1.set(o2,"azerty"); wm2.set(o1, o2); // a value can be anything, including an object or a function wm2.set(o3, undefined); wm2.set(wm1, wm2); // keys and values can be any objects. Even WeakMaps! wm1.get(o2); //"azerty" wm2.get(o2); // undefined, because there is no value for o2 on wm2 wm2.get(o3); // undefined, because that is the set value wm1.has(o2); // true wm2.has(o2); // false wm2.has(o3); // true (even if the value itself is 'undefined') wm3.set(o1, 37); wm3.get(o1); // 37 wm3.clear(); wm3.get(o1); // undefined, because wm3 was cleared and there is no value for o1 anymore wm1.has(o1); // true wm1.delete(o1); wm1.has(o1); // false |
但是:
1 | Because of references being weak, WeakMap keys are not enumerable (i.e. there is no method giving you a list of the keys). |
我已经实现了一个javascript hashmap,代码可以从http://github.com/lambdar/hashmapjs/tree/master获得。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | /* ===================================================================== @license MIT @author Lambder @copyright 2009 Lambder. @end ===================================================================== */ var HashMap = function() { this.initialize(); } HashMap.prototype = { hashkey_prefix:"<#HashMapHashkeyPerfix>", hashcode_field:"<#HashMapHashkeyPerfix>", initialize: function() { this.backing_hash = {}; this.code = 0; }, /* maps value to key returning previous assocciation */ put: function(key, value) { var prev; if (key && value) { var hashCode = key[this.hashcode_field]; if (hashCode) { prev = this.backing_hash[hashCode]; } else { this.code += 1; hashCode = this.hashkey_prefix + this.code; key[this.hashcode_field] = hashCode; } this.backing_hash[hashCode] = value; } return prev; }, /* returns value associated with given key */ get: function(key) { var value; if (key) { var hashCode = key[this.hashcode_field]; if (hashCode) { value = this.backing_hash[hashCode]; } } return value; }, /* deletes association by given key. Returns true if the assocciation existed, false otherwise */ del: function(key) { var success = false; if (key) { var hashCode = key[this.hashcode_field]; if (hashCode) { var prev = this.backing_hash[hashCode]; this.backing_hash[hashCode] = undefined; if(prev !== undefined) success = true; } } return success; } } //// Usage // creation var my_map = new HashMap(); // insertion var a_key = {}; var a_value = {struct:"structA"}; var b_key = {}; var b_value = {struct:"structB"}; var c_key = {}; var c_value = {struct:"structC"}; my_map.put(a_key, a_value); my_map.put(b_key, b_value); var prev_b = my_map.put(b_key, c_value); // retrieval if(my_map.get(a_key) !== a_value){ throw("fail1") } if(my_map.get(b_key) !== c_value){ throw("fail2") } if(prev_b !== b_value){ throw("fail3") } // deletion var a_existed = my_map.del(a_key); var c_existed = my_map.del(c_key); var a2_existed = my_map.del(a_key); if(a_existed !== true){ throw("fail4") } if(c_existed !== false){ throw("fail5") } if(a2_existed !== false){ throw("fail6") } |
javascript不内置map/hashmap。它应该称为关联数组。
它更类似于object.properties,而不是键值映射。如果您在javascript中寻找更好的键/值映射,请使用您可以在Web上找到的映射对象。
尝试我的javascript哈希表实现:http://www.timdown.co.uk/jshashtable
它查找键对象的hashcode()方法,或者在创建hashtable对象时提供一个哈希函数。
我的地图实现,源自Christoph的示例:
示例用法:
1 2 | var map = new Map(); //creates an"in-memory" map var map = new Map("storageId"); //creates a map that is loaded/persisted using html5 storage |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | function Map(storageId) { this.current = undefined; this.size = 0; this.storageId = storageId; if (this.storageId) { this.keys = new Array(); this.disableLinking(); } } Map.noop = function() { return this; }; Map.illegal = function() { throw new Error("illegal operation for maps without linking"); }; // map initialisation from existing object // doesn't add inherited properties if not explicitly instructed to: // omitting foreignKeys means foreignKeys === undefined, i.e. == false // --> inherited properties won't be added Map.from = function(obj, foreignKeys) { var map = new Map; for(var prop in obj) { if(foreignKeys || obj.hasOwnProperty(prop)) map.put(prop, obj[prop]); } return map; }; Map.prototype.disableLinking = function() { this.link = Map.noop; this.unlink = Map.noop; this.disableLinking = Map.noop; this.next = Map.illegal; this.key = Map.illegal; this.value = Map.illegal; // this.removeAll = Map.illegal; return this; }; // overwrite in Map instance if necessary Map.prototype.hash = function(value) { return (typeof value) + ' ' + (value instanceof Object ? (value.__hash || (value.__hash = ++arguments.callee.current)) : value.toString()); }; Map.prototype.hash.current = 0; // --- mapping functions Map.prototype.get = function(key) { var item = this[this.hash(key)]; if (item === undefined) { if (this.storageId) { try { var itemStr = localStorage.getItem(this.storageId + key); if (itemStr && itemStr !== 'undefined') { item = JSON.parse(itemStr); this[this.hash(key)] = item; this.keys.push(key); ++this.size; } } catch (e) { console.log(e); } } } return item === undefined ? undefined : item.value; }; Map.prototype.put = function(key, value) { var hash = this.hash(key); if(this[hash] === undefined) { var item = { key : key, value : value }; this[hash] = item; this.link(item); ++this.size; } else this[hash].value = value; if (this.storageId) { this.keys.push(key); try { localStorage.setItem(this.storageId + key, JSON.stringify(this[hash])); } catch (e) { console.log(e); } } return this; }; Map.prototype.remove = function(key) { var hash = this.hash(key); var item = this[hash]; if(item !== undefined) { --this.size; this.unlink(item); delete this[hash]; } if (this.storageId) { try { localStorage.setItem(this.storageId + key, undefined); } catch (e) { console.log(e); } } return this; }; // only works if linked Map.prototype.removeAll = function() { if (this.storageId) { for (var i=0; i<this.keys.length; i++) { this.remove(this.keys[i]); } this.keys.length = 0; } else { while(this.size) this.remove(this.key()); } return this; }; // --- linked list helper functions Map.prototype.link = function(item) { if (this.storageId) { return; } if(this.size == 0) { item.prev = item; item.next = item; this.current = item; } else { item.prev = this.current.prev; item.prev.next = item; item.next = this.current; this.current.prev = item; } }; Map.prototype.unlink = function(item) { if (this.storageId) { return; } if(this.size == 0) this.current = undefined; else { item.prev.next = item.next; item.next.prev = item.prev; if(item === this.current) this.current = item.next; } }; // --- iterator functions - only work if map is linked Map.prototype.next = function() { this.current = this.current.next; }; Map.prototype.key = function() { if (this.storageId) { return undefined; } else { return this.current.key; } }; Map.prototype.value = function() { if (this.storageId) { return undefined; } return this.current.value; }; |
这看起来是一个非常强大的解决方案:https://github.com/flesler/hashmap。它甚至可以很好地应用于外观相同的函数和对象。它使用的唯一黑客方法是在对象中添加一个模糊的成员来识别它。如果你的程序没有覆盖这个模糊的变量(比如hashid),你就是黄金。
如果性能不是关键的(例如密钥的数量相对较小),并且您不想用额外的字段(如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | var Dict = (function(){ // IE 8 and earlier has no Array.prototype.indexOf function indexOfPolyfill(val) { for (var i = 0, l = this.length; i < l; ++i) { if (this[i] === val) { return i; } } return -1; } function Dict(){ this.keys = []; this.values = []; if (!this.keys.indexOf) { this.keys.indexOf = indexOfPolyfill; } }; Dict.prototype.has = function(key){ return this.keys.indexOf(key) != -1; }; Dict.prototype.get = function(key, defaultValue){ var index = this.keys.indexOf(key); return index == -1 ? defaultValue : this.values[index]; }; Dict.prototype.set = function(key, value){ var index = this.keys.indexOf(key); if (index == -1) { this.keys.push(key); this.values.push(value); } else { var prevValue = this.values[index]; this.values[index] = value; return prevValue; } }; Dict.prototype.delete = function(key){ var index = this.keys.indexOf(key); if (index != -1) { this.keys.splice(index, 1); return this.values.splice(index, 1)[0]; } }; Dict.prototype.clear = function(){ this.keys.splice(0, this.keys.length); this.values.splice(0, this.values.length); }; return Dict; })(); |
使用示例:
10与ES6 weakmap相比,它有两个问题:o(n)搜索时间和非弱点(即,如果不使用
添加另一个解决方案:EDCOX1(4)是我从Java移植到JavaScript的第一个类。可以说,有很多开销,但是实现几乎等于Java实现的100%,并且包括所有接口和子类。
这个项目可以在这里找到:https://github.com/airblader/jsava我还将附加hashmap类的(当前)源代码,但如前所述,它还依赖于超级类等。使用的OOP框架是qooxdoo。
编辑:请注意,此代码已经过时,请参阅github项目以了解当前工作。在撰写本文时,还有一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 | qx.Class.define( 'jsava.util.HashMap', { extend: jsava.util.AbstractMap, implement: [jsava.util.Map, jsava.io.Serializable, jsava.lang.Cloneable], construct: function () { var args = Array.prototype.slice.call( arguments ), initialCapacity = this.self( arguments ).DEFAULT_INITIAL_CAPACITY, loadFactor = this.self( arguments ).DEFAULT_LOAD_FACTOR; switch( args.length ) { case 1: if( qx.Class.implementsInterface( args[0], jsava.util.Map ) ) { initialCapacity = Math.max( ((args[0].size() / this.self( arguments ).DEFAULT_LOAD_FACTOR) | 0) + 1, this.self( arguments ).DEFAULT_INITIAL_CAPACITY ); loadFactor = this.self( arguments ).DEFAULT_LOAD_FACTOR; } else { initialCapacity = args[0]; } break; case 2: initialCapacity = args[0]; loadFactor = args[1]; break; } if( initialCapacity < 0 ) { throw new jsava.lang.IllegalArgumentException( 'Illegal initial capacity: ' + initialCapacity ); } if( initialCapacity > this.self( arguments ).MAXIMUM_CAPACITY ) { initialCapacity = this.self( arguments ).MAXIMUM_CAPACITY; } if( loadFactor <= 0 || isNaN( loadFactor ) ) { throw new jsava.lang.IllegalArgumentException( 'Illegal load factor: ' + loadFactor ); } var capacity = 1; while( capacity < initialCapacity ) { capacity <<= 1; } this._loadFactor = loadFactor; this._threshold = (capacity * loadFactor) | 0; this._table = jsava.JsavaUtils.emptyArrayOfGivenSize( capacity, null ); this._init(); }, statics: { serialVersionUID: 1, DEFAULT_INITIAL_CAPACITY: 16, MAXIMUM_CAPACITY: 1 << 30, DEFAULT_LOAD_FACTOR: 0.75, _hash: function (hash) { hash ^= (hash >>> 20) ^ (hash >>> 12); return hash ^ (hash >>> 7) ^ (hash >>> 4); }, _indexFor: function (hashCode, length) { return hashCode & (length - 1); }, Entry: qx.Class.define( 'jsava.util.HashMap.Entry', { extend: jsava.lang.Object, implement: [jsava.util.Map.Entry], construct: function (hash, key, value, nextEntry) { this._value = value; this._next = nextEntry; this._key = key; this._hash = hash; }, members: { _key: null, _value: null, /** @type jsava.util.HashMap.Entry */ _next: null, /** @type Number */ _hash: 0, getKey: function () { return this._key; }, getValue: function () { return this._value; }, setValue: function (newValue) { var oldValue = this._value; this._value = newValue; return oldValue; }, equals: function (obj) { if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.HashMap.Entry ) ) { return false; } /** @type jsava.util.HashMap.Entry */ var entry = obj, key1 = this.getKey(), key2 = entry.getKey(); if( key1 === key2 || (key1 !== null && key1.equals( key2 )) ) { var value1 = this.getValue(), value2 = entry.getValue(); if( value1 === value2 || (value1 !== null && value1.equals( value2 )) ) { return true; } } return false; }, hashCode: function () { return (this._key === null ? 0 : this._key.hashCode()) ^ (this._value === null ? 0 : this._value.hashCode()); }, toString: function () { return this.getKey() + '=' + this.getValue(); }, /** * This method is invoked whenever the value in an entry is * overwritten by an invocation of put(k,v) for a key k that's already * in the HashMap. */ _recordAccess: function (map) { }, /** * This method is invoked whenever the entry is * removed from the table. */ _recordRemoval: function (map) { } } } ) }, members: { /** @type jsava.util.HashMap.Entry[] */ _table: null, /** @type Number */ _size: 0, /** @type Number */ _threshold: 0, /** @type Number */ _loadFactor: 0, /** @type Number */ _modCount: 0, /** @implements jsava.util.Set */ __entrySet: null, /** * Initialization hook for subclasses. This method is called * in all constructors and pseudo-constructors (clone, readObject) * after HashMap has been initialized but before any entries have * been inserted. (In the absence of this method, readObject would * require explicit knowledge of subclasses.) */ _init: function () { }, size: function () { return this._size; }, isEmpty: function () { return this._size === 0; }, get: function (key) { if( key === null ) { return this.__getForNullKey(); } var hash = this.self( arguments )._hash( key.hashCode() ); for( var entry = this._table[this.self( arguments )._indexFor( hash, this._table.length )]; entry !== null; entry = entry._next ) { /** @type jsava.lang.Object */ var k; if( entry._hash === hash && ((k = entry._key) === key || key.equals( k )) ) { return entry._value; } } return null; }, __getForNullKey: function () { for( var entry = this._table[0]; entry !== null; entry = entry._next ) { if( entry._key === null ) { return entry._value; } } return null; }, containsKey: function (key) { return this._getEntry( key ) !== null; }, _getEntry: function (key) { var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ); for( var entry = this._table[this.self( arguments )._indexFor( hash, this._table.length )]; entry !== null; entry = entry._next ) { /** @type jsava.lang.Object */ var k; if( entry._hash === hash && ( ( k = entry._key ) === key || ( key !== null && key.equals( k ) ) ) ) { return entry; } } return null; }, put: function (key, value) { if( key === null ) { return this.__putForNullKey( value ); } var hash = this.self( arguments )._hash( key.hashCode() ), i = this.self( arguments )._indexFor( hash, this._table.length ); for( var entry = this._table[i]; entry !== null; entry = entry._next ) { /** @type jsava.lang.Object */ var k; if( entry._hash === hash && ( (k = entry._key) === key || key.equals( k ) ) ) { var oldValue = entry._value; entry._value = value; entry._recordAccess( this ); return oldValue; } } this._modCount++; this._addEntry( hash, key, value, i ); return null; }, __putForNullKey: function (value) { for( var entry = this._table[0]; entry !== null; entry = entry._next ) { if( entry._key === null ) { var oldValue = entry._value; entry._value = value; entry._recordAccess( this ); return oldValue; } } this._modCount++; this._addEntry( 0, null, value, 0 ); return null; }, __putForCreate: function (key, value) { var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ), i = this.self( arguments )._indexFor( hash, this._table.length ); for( var entry = this._table[i]; entry !== null; entry = entry._next ) { /** @type jsava.lang.Object */ var k; if( entry._hash === hash && ( (k = entry._key) === key || ( key !== null && key.equals( k ) ) ) ) { entry._value = value; return; } } this._createEntry( hash, key, value, i ); }, __putAllForCreate: function (map) { var iterator = map.entrySet().iterator(); while( iterator.hasNext() ) { var entry = iterator.next(); this.__putForCreate( entry.getKey(), entry.getValue() ); } }, _resize: function (newCapacity) { var oldTable = this._table, oldCapacity = oldTable.length; if( oldCapacity === this.self( arguments ).MAXIMUM_CAPACITY ) { this._threshold = Number.MAX_VALUE; return; } var newTable = jsava.JsavaUtils.emptyArrayOfGivenSize( newCapacity, null ); this._transfer( newTable ); this._table = newTable; this._threshold = (newCapacity * this._loadFactor) | 0; }, _transfer: function (newTable) { var src = this._table, newCapacity = newTable.length; for( var j = 0; j < src.length; j++ ) { var entry = src[j]; if( entry !== null ) { src[j] = null; do { var next = entry._next, i = this.self( arguments )._indexFor( entry._hash, newCapacity ); entry._next = newTable[i]; newTable[i] = entry; entry = next; } while( entry !== null ); } } }, putAll: function (map) { var numKeyToBeAdded = map.size(); if( numKeyToBeAdded === 0 ) { return; } if( numKeyToBeAdded > this._threshold ) { var targetCapacity = (numKeyToBeAdded / this._loadFactor + 1) | 0; if( targetCapacity > this.self( arguments ).MAXIMUM_CAPACITY ) { targetCapacity = this.self( arguments ).MAXIMUM_CAPACITY; } var newCapacity = this._table.length; while( newCapacity < targetCapacity ) { newCapacity <<= 1; } if( newCapacity > this._table.length ) { this._resize( newCapacity ); } } var iterator = map.entrySet().iterator(); while( iterator.hasNext() ) { var entry = iterator.next(); this.put( entry.getKey(), entry.getValue() ); } }, remove: function (key) { var entry = this._removeEntryForKey( key ); return entry === null ? null : entry._value; }, _removeEntryForKey: function (key) { var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ), i = this.self( arguments )._indexFor( hash, this._table.length ), prev = this._table[i], entry = prev; while( entry !== null ) { var next = entry._next, /** @type jsava.lang.Object */ k; if( entry._hash === hash && ( (k = entry._key) === key || ( key !== null && key.equals( k ) ) ) ) { this._modCount++; this._size--; if( prev === entry ) { this._table[i] = next; } else { prev._next = next; } entry._recordRemoval( this ); return entry; } prev = entry; entry = next; } return entry; }, _removeMapping: function (obj) { if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.Map.Entry ) ) { return null; } /** @implements jsava.util.Map.Entry */ var entry = obj, key = entry.getKey(), hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ), i = this.self( arguments )._indexFor( hash, this._table.length ), prev = this._table[i], e = prev; while( e !== null ) { var next = e._next; if( e._hash === hash && e.equals( entry ) ) { this._modCount++; this._size--; if( prev === e ) { this._table[i] = next; } else { prev._next = next; } e._recordRemoval( this ); return e; } prev = e; e = next; } return e; }, clear: function () { this._modCount++; var table = this._table; for( var i = 0; i < table.length; i++ ) { table[i] = null; } this._size = 0; }, containsValue: function (value) { if( value === null ) { return this.__containsNullValue(); } var table = this._table; for( var i = 0; i < table.length; i++ ) { for( var entry = table[i]; entry !== null; entry = entry._next ) { if( value.equals( entry._value ) ) { return true; } } } return false; }, __containsNullValue: function () { var table = this._table; for( var i = 0; i < table.length; i++ ) { for( var entry = table[i]; entry !== null; entry = entry._next ) { if( entry._value === null ) { return true; } } } return false; }, clone: function () { /** @type jsava.util.HashMap */ var result = null; try { result = this.base( arguments ); } catch( e ) { if( !qx.Class.isSubClassOf( e.constructor, jsava.lang.CloneNotSupportedException ) ) { throw e; } } result._table = jsava.JsavaUtils.emptyArrayOfGivenSize( this._table.length, null ); result.__entrySet = null; result._modCount = 0; result._size = 0; result._init(); result.__putAllForCreate( this ); return result; }, _addEntry: function (hash, key, value, bucketIndex) { var entry = this._table[bucketIndex]; this._table[bucketIndex] = new (this.self( arguments ).Entry)( hash, key, value, entry ); if( this._size++ >= this._threshold ) { this._resize( 2 * this._table.length ); } }, _createEntry: function (hash, key, value, bucketIndex) { var entry = this._table[bucketIndex]; this._table[bucketIndex] = new (this.self( arguments ).Entry)( hash, key, value, entry ); this._size++; }, keySet: function () { var keySet = this._keySet; return keySet !== null ? keySet : ( this._keySet = new this.KeySet( this ) ); }, values: function () { var values = this._values; return values !== null ? values : ( this._values = new this.Values( this ) ); }, entrySet: function () { return this.__entrySet0(); }, __entrySet0: function () { var entrySet = this.__entrySet; return entrySet !== null ? entrySet : ( this.__entrySet = new this.EntrySet( this ) ); }, /** @private */ HashIterator: qx.Class.define( 'jsava.util.HashMap.HashIterator', { extend: jsava.lang.Object, implement: [jsava.util.Iterator], type: 'abstract', /** @protected */ construct: function (thisHashMap) { this.__thisHashMap = thisHashMap; this._expectedModCount = this.__thisHashMap._modCount; if( this.__thisHashMap._size > 0 ) { var table = this.__thisHashMap._table; while( this._index < table.length && ( this._next = table[this._index++] ) === null ) { // do nothing } } }, members: { __thisHashMap: null, /** @type jsava.util.HashMap.Entry */ _next: null, /** @type Number */ _expectedModCount: 0, /** @type Number */ _index: 0, /** @type jsava.util.HashMap.Entry */ _current: null, hasNext: function () { return this._next !== null; }, _nextEntry: function () { if( this.__thisHashMap._modCount !== this._expectedModCount ) { throw new jsava.lang.ConcurrentModificationException(); } var entry = this._next; if( entry === null ) { throw new jsava.lang.NoSuchElementException(); } if( (this._next = entry._next) === null ) { var table = this.__thisHashMap._table; while( this._index < table.length && ( this._next = table[this._index++] ) === null ) { // do nothing } } this._current = entry; return entry; }, remove: function () { if( this._current === null ) { throw new jsava.lang.IllegalStateException(); } if( this.__thisHashMap._modCount !== this._expectedModCount ) { throw new jsava.lang.ConcurrentModificationException(); } var key = this._current._key; this._current = null; this.__thisHashMap._removeEntryForKey( key ); this._expectedModCount = this.__thisHashMap._modCount; } } } ), _newKeyIterator: function () { return new this.KeyIterator( this ); }, _newValueIterator: function () { return new this.ValueIterator( this ); }, _newEntryIterator: function () { return new this.EntryIterator( this ); }, /** @private */ ValueIterator: qx.Class.define( 'jsava.util.HashMap.ValueIterator', { extend: jsava.util.HashMap.HashIterator, construct: function (thisHashMap) { this.base( arguments, thisHashMap ); }, members: { next: function () { return this._nextEntry()._value; } } } ), /** @private */ KeyIterator: qx.Class.define( 'jsava.util.HashMap.KeyIterator', { extend: jsava.util.HashMap.HashIterator, construct: function (thisHashMap) { this.base( arguments, thisHashMap ); }, members: { next: function () { return this._nextEntry().getKey(); } } } ), /** @private */ EntryIterator: qx.Class.define( 'jsava.util.HashMap.EntryIterator', { extend: jsava.util.HashMap.HashIterator, construct: function (thisHashMap) { this.base( arguments, thisHashMap ); }, members: { next: function () { return this._nextEntry(); } } } ), /** @private */ KeySet: qx.Class.define( 'jsava.util.HashMap.KeySet', { extend: jsava.util.AbstractSet, construct: function (thisHashMap) { this.base( arguments ); this.__thisHashMap = thisHashMap; }, members: { __thisHashMap: null, iterator: function () { return this.__thisHashMap._newKeyIterator(); }, size: function () { return this.__thisHashMap._size; }, contains: function (obj) { return this.__thisHashMap.containsKey( obj ); }, remove: function (obj) { return this.__thisHashMap._removeEntryForKey( obj ) !== null; }, clear: function () { this.__thisHashMap.clear(); } } } ), /** @private */ Values: qx.Class.define( 'jsava.util.HashMap.Values', { extend: jsava.util.AbstractCollection, construct: function (thisHashMap) { this.base( arguments ); this.__thisHashMap = thisHashMap; }, members: { __thisHashMap: null, iterator: function () { return this.__thisHashMap._newValueIterator(); }, size: function () { return this.__thisHashMap._size; }, contains: function (obj) { return this.__thisHashMap.containsValue( obj ); }, clear: function () { this.__thisHashMap.clear(); } } } ), /** @private */ EntrySet: qx.Class.define( 'jsava.util.HashMap.EntrySet', { extend: jsava.util.AbstractSet, construct: function (thisHashMap) { this.base( arguments ); this.__thisHashMap = thisHashMap; }, members: { __thisHashMap: null, iterator: function () { return this.__thisHashMap._newEntryIterator(); }, size: function () { return this.__thisHashMap._size; }, contains: function (obj) { if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.Map.Entry ) ) { return false; } /** @implements jsava.util.Map.Entry */ var entry = obj, candidate = this.__thisHashMap._getEntry( entry.getKey() ); return candidate !== null && candidate.equals( entry ); }, remove: function (obj) { return this.__thisHashMap._removeMapping( obj ) !== null; }, clear: function () { this.__thisHashMap.clear(); } } } ) } } ); |
还有一个由我实现的映射。使用随机数发生器、"泛型"和"迭代器"=)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | var HashMap = function (TKey, TValue) { var db = []; var keyType, valueType; (function () { keyType = TKey; valueType = TValue; })(); var getIndexOfKey = function (key) { if (typeof key !== keyType) throw new Error('Type of key should be ' + keyType); for (var i = 0; i < db.length; i++) { if (db[i][0] == key) return i; } return -1; } this.add = function (key, value) { if (typeof key !== keyType) { throw new Error('Type of key should be ' + keyType); } else if (typeof value !== valueType) { throw new Error('Type of value should be ' + valueType); } var index = getIndexOfKey(key); if (index === -1) db.push([key, value]); else db[index][1] = value; return this; } this.get = function (key) { if (typeof key !== keyType || db.length === 0) return null; for (var i = 0; i < db.length; i++) { if (db[i][0] == key) return db[i][1]; } return null; } this.size = function () { return db.length; } this.keys = function () { if (db.length === 0) return []; var result = []; for (var i = 0; i < db.length; i++) { result.push(db[i][0]); } return result; } this.values = function () { if (db.length === 0) return []; var result = []; for (var i = 0; i < db.length; i++) { result.push(db[i][1]); } return result; } this.randomize = function () { if (db.length === 0) return this; var currentIndex = db.length, temporaryValue, randomIndex; while (0 !== currentIndex) { randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; temporaryValue = db[currentIndex]; db[currentIndex] = db[randomIndex]; db[randomIndex] = temporaryValue; } return this; } this.iterate = function (callback) { if (db.length === 0) return false; for (var i = 0; i < db.length; i++) { callback(db[i][0], db[i][1]); } return true; } } |
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var a = new HashMap("string","number"); a.add('test', 1132) .add('test14', 666) .add('1421test14', 12312666) .iterate(function (key, value) {console.log('a['+key+']='+value)}); /* a[test]=1132 a[test14]=666 a[1421test14]=12312666 */ a.randomize(); /* a[1421test14]=12312666 a[test]=1132 a[test14]=666 */ |