提问人:Kjara 提问时间:10/5/2017 更新时间:10/5/2017 访问量:444
关于具有可变元素的字典/哈希集的注意事项
considerations on dictionaries/hashsets with mutable elements
问:
我是字典和哈希集的忠实粉丝,因为它们允许快速检查包含性,如果是字典,还可以通过键快速访问元素。
它们的缺点是它们只适用于(准)不可变对象:由于它们基于哈希,我们必须保证哈希代码(字典的键,哈希集的元素)永远不会改变,否则它们将无法再找到元素。
但有时我需要字典/哈希集的好处,并且仍然能够以某种方式更改键/元素,以便它们的哈希代码也发生变化。示例:我想在集合中存储 2d 点。2d 点由两个 int 值组成,如果两个 2d 点的 int 值相等,则它们相等。我需要能够更改点的值,但我仍然需要一种快速方法来检查集合中是否包含某个点。
所以我在想一种方法来让它发挥作用。我需要什么?
- 每当我想更改元素的数据时,我都需要 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
}
因此,我需要使用而不是常规 ,并且我需要为要更改的每条数据使用两个事件来构建 setter(只要该数据已合并到 or 方法中)。MySet
HashSet
GetHashCode
Equals
我的问题是:
您是否发现我的方法有任何错误或此方法存在任何问题?
- 代码可读性/可理解性问题
- 实施问题
- 性能问题
- 正确性问题
- ...
应该注意的是,更改数据不会经常发生。因此,我确信,能够快速检查包含性的性能提升将超过每次数据更改后更新集合对性能的影响。
答: 暂无答案
评论
Dictionary<Tuple<int, int>, Point>
Point
KeyedCollection