当我想使用自定义类型T
作为.Net中的哈希键时,我实现了IEqualityComparer并将其传递给哈希图,例如Dictionary
或HashSet
添加新项目后,将调用GetHashCode
和Equals
方法来检查两个T
实例是否相同。
例如,我有一个不变的数据类Foo
:
sealed class Foo
{
public Foo(int field1,string field2)
{
Prop_1 = field1;
Prop_2 = field2;
}
public int Prop_1 { get; }
public string Prop_2 { get; }
}
和FooEuqalityComparer
:
sealed class FooEuqalityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x,Foo y)
{
return x == null ? y == null :
x.Prop_1 == y.Prop_1 &&
x.Prop_2 == y.Prop_2;
}
public int GetHashCode(Foo obj)
{
if (obj == null)
return 0;
return obj.Prop_1.GetHashCode() ^ obj.Prop_2.GetHashCode();
}
}
测试:
var set = new HashSet<Foo>(new FooEuqalityComparer());
var foo1 = new Foo(1,"foo 1");
var not_foo2 = new Foo(1,"foo 1");
var foo3 = new Foo(3,"foo 3");
set.Add(foo1);
set.Add(not_foo2);
Assert.AreEqual(1,set.Count);
Assert.AreSame(foo1,set.Single());
set.Add(foo3);
Assert.AreEqual(2,set.Count);
如何在Node.js中做到这一点?
无法覆盖toString()
,因为我想保留对该对象的引用作为map中的键。
经过一番搜索,我意识到javascript或ECMAScript使用SameValueZero算法来比较对象,最好的方法仍然是使用字符串作为键。 所以我用两个地图来实现这一点:
class ObjectKeyMap {
/**
* @param {Object[]} keys -
* @param {function():string} keys[].getHashCode -
* @param {function(Object):T} valueSelector -
*
* @typedef {Object} T
*/
constructor(keys,valueSelector) {
const keyReferences = {};
keys.forEach(it => {
keyReferences[it.getHashCode()] = it;
});
this.keyReferences = keyReferences;
this.map = new Map(keys.map(it => [it.getHashCode(),valueSelector(it)]));
}
/**
* @param {string|{getHashCode:function():string}} key -
*
* @returns {string}
*/
_getStringKey(key) {
if (!key) {
return null;
}
if (Object.prototype.toString.call(key) === "[object String]") {
return key;
} else {
return key.getHashCode();
}
}
/**
* @param {string|{getHashCode:function():string}} key -
*
* @returns {T}
*/
get(key) {
const stringKey = this._getStringKey(key);
if (!stringKey || stringKey === "") {
return null;
}
return this.map.get(stringKey);
}
values() {
return [...this.map.values()];
}
/**
* @param {string|{getHashCode:function():string}} key -
*/
key(key) {
const stringKey = this._getStringKey(key);
if (!stringKey || stringKey === "") {
return null;
}
return this.keyReferences[stringKey];
}
keys() {
return Object.values(this.keyReferences).slice();
}
}
ObjectKeyMap
假定要用作键的对象必须具有getHashCode
函数,该函数返回身份字符串。如果使用TypeScript编写,应该更具可读性。