HashSet允许重复的项目插入 – C#

这种看起来像一个noob问题,但我无法具体find这个问题的答案。

我有这个class级:

public class Quotes{ public string symbol; public string extension } 

我正在使用这个:

 HashSet<Quotes> values = new HashSet<Quotes>(); 

不过,我可以多次添加相同的Quotes对象。 例如,我的Quotes对象的'symbol'等于'A','extension'等于'= n',这个Quotes对象在HashSet中多次出现(通过debugging模式查看Hashset)。 打电话的时候我曾经想过

 values.Add(new Quotes(symb, ext)); 

使用相同的symb和ext,将返回“false”,并且不会添加元素。 我有一种感觉,它与HashSet添加新对象时比较Quotes对象有关。 任何帮助将不胜感激!

我猜你正在创build一个具有相同值的新Quotes 。 在这种情况下,他们是不平等的。 如果它们应该被认为是相等的,则覆盖Equals和GetHashCode方法。

 public class Quotes{ public string symbol; public string extension public override bool Equals(object obj) { Quotes q = obj as Quotes; return q != null && q.symbol == this.symbol && q.extension == this.Extension; } public override int GetHashCode() { return this.symbol.GetHashCode() ^ this.extension.GetHashCode(); } } 

我曾经想过,当调用values.Add(new Quotes(symb, ext)); 使用相同的symb和ext,将返回“false”,并且不会添加元素。

不是这种情况。

HashSet将使用GetHashCodeEquals来确定对象的相等性。 现在,由于您不会在Quotes重写这些方法,因此将使用默认的System.Object的引用相等性。 每次添加一个新的Quote时,这是一个唯一的对象实例,所以HashSet将其视为一个唯一的对象。

如果重写Object.EqualsObject.GetHashCode ,它将按照您的预期工作。

HashSets首先根据由GetHashCode计算出来的哈希来比较条目。
默认实现返回一个基于对象本身的散列码(每个实例都不相同)。

只有当哈希值相同(基于实例的哈希值非常不可能)时,Equals方法才会被调用,并用于明确比较两个对象。

你必须select:

  • 将引号更改为结构
  • 在引号中覆盖GetHashCode和Equals

例:

  public override int GetHashCode() { return (this.symbol == null ? 0 : this.symbol.GetHashCode()) ^ (this.extension == null ? 0 : this.extension.GetHashCode()); } public override bool Equals(object obj) { if (Object.ReferenceEquals(this, obj)) return true; Quotes other = obj as Quotes; if (Object.ReferenceEquals(other, null)) return false; return String.Equals(obj.symbol, this.symbol) && String.Equals(obj.extension, this.extension); } 

只是想解决肯德尔的答案(不能评论一些奇怪的原因)。

 return this.symbol.GetHashCode() ^ this.extension.GetHashCode(); 

请注意,异或函数是一个非常容易碰撞的方式来组合两个散列,尤其是当它们都是相同的types(因为符号==扩展将散列为0的每个对象)。 即使不是同一types,也不可能彼此平等,这是不好的做法,习惯它可能会导致不同电器的问题。

相反,将一个散列与一个小的素数相乘,然后添加第二个散列,例如:

 return 3 * this.symbol.GetHashCode() + this.extension.GetHashCode(); 
 Quotes q = new Quotes() { symbol = "GE", extension = "GElec" }; values.Add(q); values.Add(q); 

..将两次添加相同的实例,并将在第二次返回false。

 values.Add(new Quotes() { symbol = "GE", extension = "GElec" }); values.Add(new Quotes() { symbol = "GE", extension = "GElec" }); 

..正在添加两个不同的实例碰巧公共字段具有相同的值。

如上所述,覆盖Equals和GetHashCode将纠正这个问题:

 public class Quotes { public string symbol; public string extension; public override bool Equals(object obj) { if (!(obj is Quotes)) { return false; } return (this.symbol == ((Quotes)obj).symbol) && (this.extension == ((Quotes)obj).extension); } public override int GetHashCode() { return (this.symbol.GetHashCode()) ^ (this.extension.GetHashCode()); } } 

如果你逐步debugging你的代码,你会发现values.Add调用了Quotes.Equals和Quotes.GetHashCode。

我知道这有点晚了,但是我遇到了同样的问题,在执行选定的答案时发现了一个不可接受的性能问题,特别是当你有很多logging的时候。

我发现使用Hashset和Tuple将这个过程变成一个两步过程,并最终通过一个Select进行转换要快得多。

 public class Quotes{ public string symbol; public string extension } var values = new HashSet<Tuple<string,string>>(); values.Add(new Tuple<string,string>("A","=n")); values.Add(new Tuple<string,string>("A","=n")); // values.Count() == 1 values.Select (v => new Quotes{ symbol = v.Item1, extension = v.Item2 });