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将使用GetHashCode
和Equals
来确定对象的相等性。 现在,由于您不会在Quotes
重写这些方法,因此将使用默认的System.Object
的引用相等性。 每次添加一个新的Quote时,这是一个唯一的对象实例,所以HashSet将其视为一个唯一的对象。
如果重写Object.Equals
和Object.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 });