对具有可变元素的字典/哈希集的考虑

时间:2017-10-05 08:33:16

标签: c# dictionary hashset mutable

我是字典和散列集的忠实粉丝,因为它们允许快速检查包含性,并且在字典的情况下,可以通过密钥快速访问元素。

它们的缺点是它们只适用于(准)不可变对象:由于它们基于散列,我们必须保证散列码(字典的键,散列集的元素)永远不会改变,否则他们将不再找到这些元素。

但有时我需要字典/哈希集的好处,并且仍然能够以某种方式更改键/元素,以便它们的哈希码也会发生变化。示例:我想在集合中存储2d点。 2d点由两个int值组成,如果int值相等,则两个2d点相等。我需要能够更改点的值,但我仍然需要一种快速的方法来检查集合中是否包含某个点。

所以我想到了一种让它发挥作用的方法。我需要什么?

  • 每当我想要更改元素的数据时,我需要1.从字典/集中删除元素,2。更改数据,3。再次将元素添加到字典/集中。然后,哈希码和存储元素的存储桶将始终保持同步。
  • 我还需要一种机制来自动为所有词典/集合执行此操作。否则,如果我有100个字典/集合包含一个将要更改的元素,我需要跟踪它们并单独更新每个元素。

以下是一些示例代码,用于说明我计划如何解决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(只要那个数据被合并到GetHashCodeEquals方法中。

我的问题是:

您认为我的方法有任何错误或此方法存在任何问题吗?

  • 代码可读性/可理解性问题
  • 实施问题
  • 性能问题
  • 正确性问题
  • ...

应该注意的是,改变数据不会经常发生。因此,我确信能够快速检查包含性的性能提升将超过每次数据更改后更新设备所带来的性能损失。

0 个答案:

没有答案