我试图在数学意义上构建一组集合。但是,如果我添加另一个集合,该集合等于已经在集合集合中的集合,但是由不同的对象表示,则该集合将被复制。
HashSet<HashSet<string>> setOfSets = new HashSet<HashSet<string>>();
HashSet<string> set1 = new HashSet<string>();
HashSet<string> set2 = new HashSet<string>();
set1.Add("Foo");
set1.Add("Bar");
set1.Add("Bar"); // set behavior okay, "Bar" is not duplicated
set2.Add("Foo");
set2.Add("Bar"); // now set1 == set2
setOfSets.Add(set1);
setOfSets.Add(set2); // now set1 AND set2 are in setOfSets, which is "wrong"
为什么set-logic用于字符串相等而不是HashSets本身的相等?我怎样才能以最少的努力解决这个问题。
答案 0 :(得分:1)
我被指出this的问题。虽然接受的答案有助于手动检查集合的相等性(通过HashSet<T>.SetEquals(HashSet<T>)
),但是将这个相等逻辑应用于一组集合并没有多大帮助。
然而,未被接受的答案(格雷戈里·亚当)给出了关于如何实现这一点的关键暗示,即HashSet<string>.CreateSetComparer()
。因为HashSet有一个接受相等比较器的构造函数,所以这是可行的方法:
HashSet<HashSet<string>> setOfSets =
new HashSet<HashSet<string>>(HashSet<string>.CreateSetComparer());
这告诉外部HashSet如何“正确”(在数学意义上)比较对象内部HashSet的类型(在我的情况下为HashSet<string>
)。
正如Hans Passant和Eric Lippert所指出的那样,HashSets的相等比较比较昂贵,如果它应用于嵌套的HashSet则更是如此,这可能是为什么没有选择它作为默认行为。
然而,根据Eric Lippert的说法,选择引用相等性的主要原因是HashSet对象的可变性。应用于我的示例:如果我将两个设置不同的HashSets(即!set1.SetEquals(set2)
)添加到我的setOfSets,然后将set1的内容更改为等于set2,setOfSets中仍然有两个集合,尽管应该只有然后一套。所以这导致了一个与原始要求不一致的状态(“集合中不应该有两个相同的对象”)。
字符串是不可变的,因此不能用字符串完成。如果我将两个不同的字符串对象“Foo”和“Bar”添加到HashSet中,则没有合法的方法可以将它们更改为相等。