使用可变键从 collection 检索项目的有效方法

Efficient way of retrieving an item from a collection with a mutable key

我有 collection 个项目 Foos,其中 属性 FooPosition,我需要通过他们的位置快速访问 Foos

例如:检索位于 X=0 和 Y=1 的 Foo

我的第一个想法是为此目的使用字典并使用 FooPosition 作为字典键。我知道我的 collection 中的每个 FooPosition 都是唯一的,如果不是这样,我不介意抛出异常。

只要 Foos 不到处乱动就可以正常工作。

但是,由于我找到了困难的方法,并且由于 and this 的帖子而理解,如果 FooPosition 更新,这将不再起作用。 我不应该在字典中使用可变键:字典将 FooPosition HashCode 保留在内存中,但不会在底层 [=35] 时更新它=]FooPosition被修改。因此,调用 dic[Position(0,1)] 给我 Foo ,它在构建字典时处于这个位置。

所以,我现在想知道我应该使用什么来有效地检索 Foos 的位置。

我所说的高效是指每次按位置查询 Foo 时不会遍历整个 collection。是否有适合可变键的结构?

感谢您的帮助

编辑

正如评论中正确提到的那样,我的问题中缺少了一部分:我无法控制 Foo 移动。该软件实际上通过 COM 协议连接到另一个软件(Excel 通过 VSTO),该协议更改 FooPosition(Excel 范围)而不通知更改。

因此,如果发生移动,我无法采取任何行动,因为我不知道确实发生了变化。

public class FooManager
{
    public void DoSomething(IList<Foo> foos) {
        Dictionary<FooPosition, Foo> fooPositionDictionary = foos.ToDictionary(x => x.Position, x => x); //I know that position is unique

        //Move Foos all around the place by changing their positions. 

        FooPosition queryPosition = new FooPosition(0, 1);

        fooPositionDictionary.TryGetValue(queryPosition, out var foo1); //DOES NOT WORK
        var foo2 = foos.FirstOrDefault(x => x.Position == queryPosition); //NOT EFFICIENT

        //Any better idea?
    }
}

public class Foo
{
    public string Name { get; set; }
    public FooPosition Position { get; set; }
}

public class FooPosition : IEquatable<FooPosition>
{
    public int X { get; set; }
    public int Y { get; set; }

    public FooPosition(int x, int y)
    {
        X = x;
        Y = y;
    }

    public void MoveBy(int i)
    {
        X = X + i;
        Y = Y + i;
    }

    public bool Equals(FooPosition other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return X == other.X && Y == other.Y;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((FooPosition) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return (X * 397) ^ Y;
        }
    }

    public static bool operator ==(FooPosition left, FooPosition right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(FooPosition left, FooPosition right)
    {
        return !Equals(left, right);
    }
}

在某种意义上,字典 - 与任何其他基于散列的数据存储一样 - 使用某种缓存。在这种情况下,哈希被缓存。然而,对于每个缓存,您都需要一些 常量 数据,这些数据在该数据存储的生命周期内不会改变。如果没有这样的常量数据,就无法有效地缓存该数据。

所以你最终将所有项目存储在某个线性集合中 - 例如a List<T>- 并一次又一次地迭代该列表。