我是字典和散列集的忠实粉丝,因为它们允许快速检查包含性,并且在字典的情况下,可以通过密钥快速访问元素。
它们的缺点是它们只适用于(准)不可变对象:由于它们基于散列,我们必须保证散列码(字典的键,散列集的元素)永远不会改变,否则他们将不再找到这些元素。
但有时我需要字典/哈希集的好处,并且仍然能够以某种方式更改键/元素,以便它们的哈希码也会发生变化。示例:我想在集合中存储2d点。 2d点由两个int值组成,如果int值相等,则两个2d点相等。我需要能够更改点的值,但我仍然需要一种快速的方法来检查集合中是否包含某个点。
所以我想到了一种让它发挥作用的方法。我需要什么?
以下是一些示例代码,用于说明我计划如何解决2d点和集合的问题:
interface IChangeable<T>
{
event EventHandler<T> OnStartChange; // must be run before changing data
event EventHandler<T> OnEndChange; // must be run after changing data
}
class Point : IChangeable<Point>
{
private int x;
private int y;
public Point(int a, int b) { x = a; y = b; }
public event EventHandler<Point> OnStartChange;
public event EventHandler<Point> OnEndChange;
public int X
{
get => x;
set
{
if (x.Equals(value)) // no change => no need to do anything
return;
OnStartChange?.Invoke(this, this); // remove current point from set
x = value;
OnEndChange?.Invoke(this, this); // add changed point to set
}
}
public int Y { ... } // analogous to X
public override int GetHashCode() { return x + 2 * y; }
public override bool Equals(object obj)
{
Point other = obj as Point;
if (other == null)
return false;
return (x.Equals(other.x) && y.Equals(other.y));
}
}
class MySet<T> : ISet<T> where T : class, IChangeable<T>
{
HashSet<T> internalSet;
public MySet() { internalSet = new HashSet<T>(); }
public bool Add(T item)
{
if (!internalSet.Add(item)) // if already contained, do nothing
return false;
item.OnStartChange += TakeOut; // else subscribe to data change events
item.OnEndChange += PutIn;
return true;
}
public bool Remove(T item)
{
if (!internalSet.Remove(item)) // if not contained, do nothing
return false;
item.OnStartChange -= TakeOut; // else unsubscribe to data change events
item.OnEndChange -= PutIn;
return true;
}
public bool Contains(T item) { return internalSet.Contains(item); }
private void TakeOut(object obj, T elem) { internalSet.Remove(elem); }
private void PutIn(object obj, T elem) { internalSet.Add(elem); }
... // implement the rest of ISet<T> interface
}
所以我需要使用MySet
而不是常规HashSet
,我需要为我想要更改的每个数据构建使用这两个事件的setter(只要那个数据被合并到GetHashCode
或Equals
方法中。
我的问题是:
您认为我的方法有任何错误或此方法存在任何问题吗?
应该注意的是,改变数据不会经常发生。因此,我确信能够快速检查包含性的性能提升将超过每次数据更改后更新设备所带来的性能损失。