那里有没有好的JavaScript哈希(代码/表)实现?

时间:2008-10-22 11:32:30

标签: javascript hash

是的,我知道你可以在JavaScript中使用常规对象作为关联数组,但是我想使用更接近java的Map实现的东西(HashMap,LinkedHashMap等)。可以将任何类型的数据用作密钥的东西。在JavaScript实现中是否有任何好的哈希(代码/表)?

4 个答案:

答案 0 :(得分:23)

在javascript中,对象实际上是一个哈希实现。 Java HashMap会有点虚假,所以我挑战你来重新考虑你的需求。

直接答案是否,我不相信在javascript中有很好的Java HashMap实现。如果存在,它必然是您可能想要或可能不想使用的库的一部分,并且您当然不需要包含库只是为了拥有一个哈希表。

所以让我们继续写一个,只是为了检查问题。如果你愿意,你可以使用它。我们将首先编写一个构造函数,然后我们将躲避Array,这是一个Object,但它有一些有用的方法可以防止这个例子过于繁琐:

function HashMap () {
    var obj = [];
    return obj;
}

var myHashMap = HashMap();

我们将直接从Java世界中添加一些方法,但在我们去的时候转换为javascript ...

function HashMap() {
    var obj = [];
    obj.size = function () {
        return this.length;
    };
    obj.isEmpty = function () {
        return this.length === 0;
    };
    obj.containsKey = function (key) {
        for (var i = 0; i < this.length; i++) {
            if (this[i].key === key) {
                return i;
            }
        }
        return -1;
    };
    obj.get = function (key) {
        var index = this.containsKey(key);
        if (index > -1) {
            return this[index].value;
        }
    };
    obj.put = function (key, value) {
        if (this.containsKey(key) !== -1) {
            return this.get(key);
        }
        this.push({'key': key, 'value': value});
    };
    obj.clear = function () {
        this = null;  // Just kidding...
    };
    return obj;
}

我们可以继续构建它,但我认为这是错误的方法。在一天结束时,我们最终使用javascript在幕后提供的内容,因为我们只是没有HashMap类型。在假装的过程中,它适用于各种额外工作

有点讽刺的是,使javascript成为一种有趣且多样化的语言之一就是它能够轻松处理这种摔跤。我们可以从字面上做任何我们想做的事情,如果它没有说明语言的欺骗性力量,那么这里的快速例子什么都不做。然而,鉴于这种力量,似乎最好不要使用它

我只是觉得javascript想要更轻松。我个人建议您在尝试实现适当的Java HashMap之前重新检查问题。 Javascript既不想要也不提供

请记住原生替代

var map = [{}, 'string', 4, {}];

..相比之下如此快速和简单。

另一方面,我不相信这里有任何硬性答案。这个实现真的可能是一个完全可以接受的解决方案。如果你觉得你可以使用它,我会说给它一个旋转。但是如果我觉得我们拥有相当简单和更自然的手段,我就永远不会使用它......我几乎可以肯定我们这样做了。

<强>旁注: 效率与风格有关吗?请注意性能提升 ..在HashMap.put()中有一个很大的O盯着我们......不太理想的性能可能不是一个显示阻止在这里,而你在您甚至注意到现代浏览器的性能提升之前,可能需要做一些非常雄心勃勃的事情或拥有大量数据。值得注意的是,当你对抗谷物时,操作往往效率会降低,几乎就像工作中存在自然熵一样。 Javascript是一种高级语言,当我们遵守其约定时应该提供有效的解决方案,就像Java中的HashMap将是一个更加自然和高性能的选择。

答案 1 :(得分:7)

我发布了一个独立的JavaScript哈希表实现,比这里列出的更为实用。

http://www.timdown.co.uk/jshashtable/

答案 2 :(得分:1)

请注意,使用“任何类型的对象”作为键的java集合并不完全正确。是的,您可以使用任何对象,但除非该对象具有良好的hashcode()和equals()实现,否则它将无法正常工作。基类Object类具有这些的默认实现,但是要使自定义类(有效地)作为哈希表键工作,您需要覆盖它们。 Javascript没有等价物(我知道)。

要在javascript中创建一个哈希表,它可以(有效地)使用任意对象作为键,您需要在您使用的对象上强制执行类似的操作,至少如果您想保持哈希表的性能增益。如果你可以强制执行返回String的'hashcode()'方法,那么你可以使用引擎盖下的Object作为实际的哈希表。

否则,您需要像发布的其他解决方案一样,现在不会像哈希表那样执行。他们都在列表上进行O(n)搜索以尝试找到密钥,这几乎违背了哈希表的目的(哈希表通常是获取/放置的恒定时间)。

答案 3 :(得分:0)

这是我刚刚整理的一个天真的实现 - 正如keparo在评论中提到的,其中一个重要问题是平等检查:

var ObjectMap = function()
{
    this._keys = [];
    this._values = [];
};

ObjectMap.prototype.clear = function()
{
    this._keys = [];
    this._values = [];
};

ObjectMap.prototype.get = function(key)
{
    var index = this._indexOf(key, this._keys);
    if (index != -1)
    {
        return this._values[index];
    }
    return undefined;
};

ObjectMap.prototype.hasKey = function(key)
{
    return (this._indexOf(key, this._keys) != -1);
};

ObjectMap.prototype.hasValue = function(value)
{
    return (this._indexOf(value, this._values) != -1);
};

ObjectMap.prototype.put = function(key, value)
{
    var index = this._indexOf(key, this._keys);
    if (index == -1)
    {
        index = this._keys.length;
    }

    this._keys[index] = key;
    this._values[index] = value;
};

ObjectMap.prototype.remove = function(key)
{
    var index = this._indexOf(key, this._keys);
    if (index != -1)
    {
        this._keys.splice(index, 1);
        this._values.splice(index, 1);
    }
};

ObjectMap.prototype.size = function()
{
    return this._keys.length;
};

ObjectMap.prototype._indexOf = function(item, list)
{
    for (var i = 0, l = list.length; i < l; i++)
    {
        if (this._equals(list[i], item))
        {
            return i;
        }
    }
    return -1;
};

ObjectMap.prototype._equals = function(a, b)
{
    if (a === b)
    {
        return true;
    }

    // Custom objects can implement an equals method
    if (typeof a.equals == "function" &&
        typeof b.equals == "function")
    {
        return a.equals(b);
    }

    // Arrays are equal if they're the same length and their contents are equal
    if (a instanceof Array && b instanceof Array)
    {
        if (a.length != b.length)
        {
            return false;
        }

        for (var i = 0, l = a.length; i < l; i++)
        {
            if (!this._equals(a[i], b[i]))
            {
                return false;
            }
        }

        return true;
    }

    // Checking object properties - objects are equal if they have all the same
    // properties and they're all equal.
    var seenProperties = {};
    for (var prop in a)
    {
        if (a.hasOwnProperty(prop))
        {
            if (!b.hasOwnProperty(prop))
            {
                return false;
            }

            if (!this._equals(a[prop], b[prop]))
            {
                return false;
            }

            seenProperties[prop] = true;
        }
    }

    for (var prop in b)
    {
        if (!(prop in seenProperties) && b.hasOwnProperty(prop))
        {
            if (!a.hasOwnProperty(prop))
            {
                return false;
            }

            if (!this._equals(b[prop], a[prop]))
            {
                return false;
            }
        }
    }

    return true;
};

使用示例:

>>> var map = new ObjectMap();
>>> var o = {a: 1, b: [1,2], c: true};
>>> map.put(o, "buns");
>>> map.get(o)
"buns"
>>> map.get({a: 1, b: [1,2], c: true});
"buns"
>>> map.get({a: 1, b: [1,2], c: true, d:"hi"});
>>> var a = [1,2,3];
>>> map.put(a, "cheese");
>>> map.get(a);
"cheese"
>>> map.get([1,2,3]);
"cheese"
>>> map.get([1,2,3,4]);
>>> var d = new Date();
>>> map.put(d, "toast");
>>> map.get(d);
"toast"
>>> map.get(new Date(d.valueOf()));
"toast"

这绝不是一个完整的实现,只是一个实现这种对象的方法的指针。例如,查看我给出的内容,您还需要在对象属性检查之前添加构造函数属性检查,因为这当前有效:

>>> function TestObject(a) { this.a = a; };
>>> var t = new TestObject("sandwich");
>>> map.put(t, "butter");
>>> map.get({a: "sandwich"})
"butter"