HashSets不保留的元素独特的,如果你发生变异自己的身份(HashSets don't

2019-07-30 08:04发布

当工作HashSets在C#中,我最近碰到一个恼人的问题: HashSets不保证内容的唯一性; 他们不是集。 他们做什么保证的是,当Add(T item)被称为项目,如果没有在任何一组产品加入item.equals(that)true 。 如果你已经在集合操作项目这个拥有不再。 一个小程序,演示了(从我Linqpad copypasta):

void Main()
{
    HashSet<Tester> testset = new HashSet<Tester>();
    testset.Add(new Tester(1));
    testset.Add(new Tester(2));
    foreach(Tester tester in testset){
      tester.Dump();
    }
    foreach(Tester tester in testset){
      tester.myint = 3;
    }
    foreach(Tester tester in testset){
      tester.Dump();
    }
    HashSet<Tester> secondhashset = new HashSet<Tester>(testset);
    foreach(Tester tester in secondhashset){
      tester.Dump();
    }
}

class Tester{
  public int myint;

  public Tester(int i){
    this.myint = i;
  }

  public override bool Equals(object o){
    if (o== null) return false;
    Tester that = o as Tester;
    if (that == null) return false;
    return (this.myint == that.myint);
  }

  public override int GetHashCode(){
    return this.myint;
  }

  public override string ToString(){
    return this.myint.ToString();
  }
}

它会高兴地操纵集合中的项是相等的,只是过滤出来时,一个新的HashSet建成。 什么是advicible当我想带套,我需要知道的条目是唯一的工作吗? 推出自己的,其中添加(T项目)增加了一个副本掉的项目,枚举枚举,并通过包含项目的副本? 这就提出每个包含的元素应该是深拷贝,但至少在其项目影响是平等的挑战。

另一个解决方案是推出自己的,并只接受执行INotifyPropertyChanged元素,并采取行动的情况下,重新检查平等,但是这似乎严重限制,更不用提了一大堆的工作和性能损失的引擎盖下。

另一种可能的解决方案我想到的是确保所有字段都是只读或const在构造函数中。 所有的解决方案似乎有非常大的缺点。 我还有别的选择吗?

Answer 1:

你真的在谈论对象的身份。 如果你打算凑项目他们需要有某种形式的身份,使他们能够进行比较。

  • 如果改变了,它不是一个有效的身份证方法。 您现在有public int myint 。 这真的应该是readonly ,只有在构造函数中设置。
  • 如果两个对象是不同的概念(例如,你希望把他们在具体设计为不同的),那么它们的散列码应该是不同的。
  • 如果你有相同的内容(即具有相同的字段值即,两个值对象)两个对象那么他们应该具有相同的哈希码,应该是平等的。
  • 如果您的数据模型说,你可以有相同内容的两个对象,但它们不能是平等的,你应该使用代理ID,而不是哈希的内容。
  • 也许你的目标应该是一成不变的值类型,因此对象不能改变
  • 如果它们是可变类型,你应该指定一个代理ID(即外部引入,就像一个增大计数器ID或使用对象的哈希码即一个)从来没有给定对象的变化

这是你的一个问题Tester的对象,而不是集。 你需要认真想想你如何定义的身份。 这不是一个简单的问题。



Answer 2:

当我需要保证唯一项目的一维收集我通常去Dictionary<TKey, Tvalue> :你不能用相同的添加元素Key ,再加上我平时需要附加一些属性的物品和Value就派上用场了(我去到值类型为Tuple<>对于很多值...)。

当然,这不是最高效的,也不是最消耗内存解决方案,但我通常不会有性能/内存的担忧。



Answer 3:

你应该实现自己的IEqualityComparer ,并把它传递给一个HashSet的构造,以确保您获得所需的相等比较。

正如乔说,如果你想收集保持独特甚至超越.Add(T item) ,您需要使用由构造函数创建的,并没有公开可见的属性集ValueObjects。 即



文章来源: HashSets don't keep the elements unique if you mutate their identity