public sealed class MathSet<T> : HashSet<T>, IEquatable<MathSet<T>>
{
public override int GetHashCode() => this.Select(elt => elt.GetHashCode()).Sum().GetHashCode();
public bool Equals(MathSet<T> obj) => SetEquals(obj);
public override bool Equals(object obj) => Equals(obj as MathSet<T>);
public static bool operator ==(MathSet<T> a, MathSet<T> b) =>
ReferenceEquals(a, null) ? ReferenceEquals(b, null) : a.Equals(b);
public static bool operator !=(MathSet<T> a, MathSet<T> b) => !(a == b);
}
用法示例:
var a = new MathSet<int> { 1, 2, 3 };
var b = new MathSet<int> { 3, 2, 1 };
var c = a.Equals(b); // true
var d = new MathSet<MathSet<int>> { a, b }; // contains one element
var e = a == b; // true
每个试验包括向每个集合添加0到9,999的整数 . 但是,mod 25适用于每个整数 . Mod 25生成最大类型的项目25.由于添加了10,000个元素,因此强制发生400次冲突,使数据结构有机会使用其搜索算法 . 在10,000次试验后测量3次并取平均值 .
不要过多关注测试的具体运行时间,因为它们依赖于我的硬件,但要看它们如何相互比较 .
Average time [ms]
----------------------------
HashSet<T> 2,290
List<T> 5,505
现在让我们创建元素对象而不是基本类型 . 我写了一个包含三个字段的快速 Person 类: Name , LastName 和 ID . 由于我没有包含任何比较对象的特定方法,因此将添加所有元素而不会发生冲突 . 这个时间1,000个 Person 个对象被添加到每个集合中进行一次试验 . 平均每组3次1,000次试验的总次数 .
Average time [ms]
----------------------------
HashSet<Person> 201
List<Person> 3,000
10 回答
在CodePlex上查看PowerCollections . 除了Set和OrderedSet之外,它还有一些其他有用的集合类型,如Deque,MultiDictionary,Bag,OrderedBag,OrderedDictionary和OrderedMultiDictionary .
对于更多的集合,还有C5 Generic Collection Library .
我在
Dictionary<T, object>
周围使用了一个包装器,在值中存储了空值 . 这使得O(1)在键上添加,查找和删除,并且所有意图和目的都像集合一样 .If you're using .NET 4.0 or later:
如果您需要排序,请使用SortedSet<T> . 否则,如果不这样做,则使用HashSet<T>,因为它是
O(1)
用于搜索和操作操作 . 而SortedSet<T>
是O(log n)
用于搜索和操作操作 .正如其他人所提到的那样,.NET标准中似乎没有set set语义的set实现 .
有关为什么
HashSet
并不总是需要作为集合实现,请参见this question .如果您有兴趣滚动自己的集合类,这里有一个简单的方法:
用法示例:
试试HashSet:
我知道这是一个旧线程,但我遇到了同样的问题,并发现HashSet非常不可靠,因为给定相同的种子,GetHashCode()返回不同的代码 . 所以,我想,为什么不使用List并隐藏这样的add方法
因为List仅使用Equals方法来确定相等性,所以可以在T类型上定义Equals方法以确保获得所需的结果 .
如果您使用的是.NET 3.5,则可以使用HashSet<T> . 尽管如此,它仍然适用于集合 .
Wintellect PowerCollections也可能有所帮助 .
The HashSet<T> data structure:
Framework Framework Library的
HashSet<T>
数据结构是在.NET Framework 3.5中引入的 . 可在MSDN reference page for HashSet<T>找到其成员的完整列表 .HashSet<T>
或多或少是在mathematical set之后建模的,这意味着:它可能不包含重复值 .
其元素没有特别的顺序;因此该类型不实现IList<T>接口,但更基本的ICollection<T> . 因此,散列集内的元素不能通过索引随机访问;它们只能通过枚举器进行迭代 .
某些设定功能,例如
Union
,Intersection
,IsSubsetOf
,IsSupersetOf
可用 . 当使用多组时,这些可以派上用场 .HashSet<T>
和List<T>
之间的另一个区别是调用哈希集的Add(item)
方法返回一个布尔值:如果项目已添加则返回true
,否则返回false
(因为它已在集合中找到) .Why not List<T>?
由于
HashSet<T>
只是一个唯一对象的集合,您可能想知道为什么它必须是一个数据结构 . 正常List<T>
可以通过在添加之前检查列表中是否找到对象来具有相同的行为 .简短的回答是速度 . 随着更多元素的添加,搜索正常
List<T>
变得非常慢 .HashSet<T>
需要一种结构设计,以便快速搜索和插入速度 .Benchmarks:
让我们比较
HashSet<T>
与List<T>
的性能速度 .每个试验包括向每个集合添加0到9,999的整数 . 但是,mod 25适用于每个整数 . Mod 25生成最大类型的项目25.由于添加了10,000个元素,因此强制发生400次冲突,使数据结构有机会使用其搜索算法 . 在10,000次试验后测量3次并取平均值 .
不要过多关注测试的具体运行时间,因为它们依赖于我的硬件,但要看它们如何相互比较 .
现在让我们创建元素对象而不是基本类型 . 我写了一个包含三个字段的快速
Person
类:Name
,LastName
和ID
. 由于我没有包含任何比较对象的特定方法,因此将添加所有元素而不会发生冲突 . 这个时间1,000个Person
个对象被添加到每个集合中进行一次试验 . 平均每组3次1,000次试验的总次数 .正如您所看到的,运行时间的差异在使用对象时变得天文数字,这使得
HashSet<T>
具有优势 .您可以在几个小时内实现自己的可行集实现 . 我必须这样做时使用了这个(抱歉,我没有方便的代码):http://java.sun.com/j2se/1.4.2/docs/api/java/util/Set.html
我用Iesi.Collections http://www.codeproject.com/KB/recipes/sets.aspx
它在许多OSS项目中使用,我首先在NHibernate中遇到它