Javascript HashTable使用Object键

Ily*_*man 37 javascript hash key object

我想创建一个哈希表,将我的对象作为他的密钥,而不将其转换为String.

有点像这样:

var object1 = new Object();
var object2 = new Object();

var myHash = new HashTable();

myHash.put(object1, "value1");
myHash.put(object2, "value2");

alert(myHash.get(object1), myHash.get(object2)); // I wish that it will print value1 value2
Run Code Online (Sandbox Code Playgroud)

编辑:请参阅我的答案以获得完整的解决方案

Pet*_*ter 22

这是一个简单的Map实现,可以使用任何类型的键,包括对象引用,它不会以任何方式改变键:

function Map() {
    var keys = [], values = [];

    return {
        put: function (key, value) {
            var index = keys.indexOf(key);
            if(index == -1) {
                keys.push(key);
                values.push(value);
            }
            else {
                values[index] = value;
            }
        },
        get: function (key) {
            return values[keys.indexOf(key)];
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

虽然这会产生与哈希表相同的功能,但它实际上并没有使用哈希函数实现,因为它迭代数组并且具有最差的O(n)性能.但是,对于绝大多数明智的用例而言,这根本不应成为问题.该indexOf功能由JavaScript引擎实现,并且经过高度优化.

  • 对于未来的读者:请注意,`Map` 不再是一个好名字,因为它与 [内置 `Map` 类型](https://developer.mozilla.org/en-US/docs/Web/ JavaScript/Reference/Global_Objects/Map)。 (3认同)

Flo*_*ine 18

这是一个提议:

function HashTable() {
    this.hashes = {};
}

HashTable.prototype = {
    constructor: HashTable,

    put: function( key, value ) {
        this.hashes[ JSON.stringify( key ) ] = value;
    },

    get: function( key ) {
        return this.hashes[ JSON.stringify( key ) ];
    }
};
Run Code Online (Sandbox Code Playgroud)

API完全如您的问题所示.

但是你不能在js中使用引用(因此两个空对象看起来与hashtable相同),因为你无法得到它.有关更多详细信息,请参阅此答案:如何获取javascript对象引用或引用计数?

Jsfiddle演示:http://jsfiddle.net/HKz3e/

但是,对于事物的独特方面,您可以使用原始对象,就像这样:

function HashTable() {
    this.hashes = {},
    this.id = 0;
}

HashTable.prototype = {
    constructor: HashTable,

    put: function( obj, value ) {
        obj.id = this.id;
        this.hashes[ this.id ] = value;
        this.id++;
    },

    get: function( obj ) {
        return this.hashes[ obj.id ];
    }
};
Run Code Online (Sandbox Code Playgroud)

Jsfiddle演示:http://jsfiddle.net/HKz3e/2/

这意味着您的对象需要具有一个id您不会在其他地方使用的属性.如果你想让这个属性不可枚举,我建议你看看defineProperty(它不是跨浏览器,但是,即使使用ES5-Shim,它在IE7中也不起作用).

这也意味着您可以在此哈希表中存储的项目数量受限.限于2 53,即.

现在,"它无法在任何地方工作"解决方案:使用ES6 WeakMaps.它们完全是为了这个目的而完成的:将对象作为键.我建议您阅读MDN以获取更多信息:https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/WeakMap

它与你的API略有不同(它是set和不是put):

var myMap = new WeakMap(),
    object1 = {},
    object2 = {};

myMap.set( object1, 'value1' );
myMap.set( object2, 'value2' );

console.log( myMap.get( object1 ) ); // "value1"
console.log( myMap.get( object2 ) ); // "value2"
Run Code Online (Sandbox Code Playgroud)

带有弱地图垫片的Jsfiddle演示:http://jsfiddle.net/Ralt/HKz3e/9/

但是,弱映射是在FF和Chrome中实现的(当您在chrome中启用"实验性javascript功能"标志时).有垫片可用,如下所示:https://gist.github.com/1269991.使用风险由您自己承担.

您也可以使用Maps它们,它们可能更适合您的需求,因为您还需要将原始值(字符串)存储为键.Doc,Shim.

  • 这是错误的*.您不能依赖JSON.stringify()中键的顺序.对于具有相同键和值的不同对象,不保证使用JSON.stringify()返回相同的字符串(即使它们"大多数时间"都这样做).请参阅[javascript - 是否存在JSON.stringify的确定性等价物?](http://stackoverflow.com/questions/8931967/is-there-a-deterministic-equivalent-of-json-stringify)以获得更好的效果(但不是-trivial-JSON.stringify())对象的字符串. (22认同)
  • @FlorianMargaine我没有在那个问题的任何地方读到它应该是同一个实现的确定性.但情况似乎如此.我_did_但是读到[MDN的字符串化页面](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)说"不保证非数组对象的属性以任何特定的顺序进行字符串化.不要依赖于字符串化中同一对象内的属性排序." 即使今天有效,但明天也无法保证. (3认同)

Ily*_*man 11

我把@Florian Margaine的建议提升到了更高的水平,并想出了这个:

function HashTable(){
    var hash = new Object();
    this.put = function(key, value){
        if(typeof key === "string"){
            hash[key] = value;
        }
        else{
            if(key._hashtableUniqueId == undefined){
                key._hashtableUniqueId = UniqueId.prototype.generateId();
            }
            hash[key._hashtableUniqueId] = value;
        }

    };

    this.get = function(key){
        if(typeof key === "string"){
            return hash[key];
        }
        if(key._hashtableUniqueId == undefined){
            return undefined;
        }
        return hash[key._hashtableUniqueId];
    };
}

function UniqueId(){

}

UniqueId.prototype._id = 0;
UniqueId.prototype.generateId = function(){
    return (++UniqueId.prototype._id).toString();
};
Run Code Online (Sandbox Code Playgroud)

用法

var map = new HashTable();
var object1 = new Object();
map.put(object1, "Cocakola");
alert(map.get(object1)); // Cocakola

//Overriding
map.put(object1, "Cocakola 2");
alert(map.get(object1)); // Cocakola 2

// String key is used as String     
map.put("myKey", "MyValue");
alert(map.get("myKey")); // MyValue
alert(map.get("my".concat("Key"))); // MyValue

// Invalid keys 
alert(map.get("unknownKey")); // undefined
alert(map.get(new Object())); // undefined
Run Code Online (Sandbox Code Playgroud)

  • 对于不相同的对象而言,失败恰恰就是重点. (7认同)
  • 这要求在调用`put()`时始终保持对用作键的对象的引用.如果你打算这样做,为什么要首先映射值; 为什么不只是保持对值的引用呢?您是否考虑过如果您有两个对象相同但不是同一个对象(!==)会发生什么?它们应该散列到相同的值,但是`get()`方法将使你失败的任何有效键不是`put()`中使用的确切对象,因为你只将`._hashtableUniqueId`属性添加到其中一个. (2认同)

归档时间:

查看次数:

48134 次

最近记录:

7 年,10 月 前