这种看起来像是一个菜鸟问题,但我没有具体找到这个问题的答案。
我有这堂课:
public class Quotes{
public string symbol;
public string extension
}
我正在使用它:
HashSet<Quotes> values = new HashSet<Quotes>();
但是我可以多次添加相同的Quotes对象。例如,我的Quotes对象可能具有等于'A'的'symbol'和'= n'的'extension',并且此Quotes对象在HashSet中出现多次(通过调试模式查看Hashset)。在调用
时我曾想过values.Add(new Quotes(symb, ext));
使用相同的symb和ext,将返回“false”并且不会添加该元素。我感觉它与HashSet添加新对象时比较Quotes对象有关。任何帮助将不胜感激!
答案 0 :(得分:50)
我猜你正在创建一个具有相同值的新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();
}
}
答案 1 :(得分:19)
我原以为在使用相同的symb和ext调用
values.Add(new Quotes(symb, ext));
时,会返回'false'并且不会添加该元素。
事实并非如此。
HashSet将使用GetHashCode
和Equals
来确定对象的相等性。现在,由于您未在Quotes
中覆盖这些方法,因此将使用默认的System.Object
引用相等性。每次添加新的Quote时,它都是一个唯一的对象实例,因此HashSet将其视为唯一对象。
如果您覆盖Object.Equals
和Object.GetHashCode
,它将按预期工作。
答案 2 :(得分:6)
HashSets首先根据由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);
}
答案 3 :(得分:4)
只是想在肯德尔的答案中解决问题(不能因某些奇怪的原因发表评论)。
return this.symbol.GetHashCode() ^ this.extension.GetHashCode();
请注意,xor函数是一种特别容易碰撞的组合两个哈希的方法,特别是当它们都是相同类型时(因为symbol == extension的每个对象都会哈希为0)。即使它们不是同一类型或不太可能彼此相等,这也是不好的做法,习惯它可能会导致不同设备出现问题。
相反,将一个散列与一个小素数相乘,然后加上第二个,例如:
return 3 * this.symbol.GetHashCode() + this.extension.GetHashCode();
答案 4 :(得分:2)
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());
}
}
如果您逐步调试代码,您会发现values.Add调用Quotes.Equals和Quotes.GetHashCode。
答案 5 :(得分:2)
我知道这有点晚了,但我遇到了同样的问题,并且在实施所选答案时发现了令人无法接受的性能损失,尤其是当您有大量记录时。
我发现使用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 });
答案 6 :(得分:0)
有人建议我重写Equals()和GetHashCode()不是一个好习惯。
类是引用类型,而结构是值类型。更改为结构将允许按值进行相等比较,从而使相同的符号/扩展名相等。
public struct Quotes {
public string symbol;
public string extension;
}
public static void Main()
{
var hashSet = new HashSet<Quotes>();
hashSet.Add(new Quotes { symbol = "aaa", extension = "bbb" });
hashSet.Add(new Quotes { symbol = "aaa", extension = "bbb" });
Console.WriteLine(hashSet.Count);
}
输出为1。