在.NET 3.5之前,框架中根本没有公开集(set)集合。如果要在.NET 2.0中表示集,通常会 使用 Dictionary<,> ,用集的项作为键,用假数据作为值。.NET3.5的 HashSet 在一定程度 上改变了这一局面,现在.NET 4还添加了 SortedSet 和通用的 ISet 接口。尽管在逻辑上, 集接口应该只包含 Add / Remove / Contains 操作,但 ISet 还指定了很多其他操作来控制集 ( ExceptWith 、 IntersectWith 、 SymmetricExceptWith 和 UnionWith )并在各种复杂条件 下验证集( SetEquals 、 Overlaps 、 IsSubsetOf 、 IsSupersetOf 、 IsProperSubsetOf 和 IsProperSupersetOf )。所有这些方法的参数均为 IEnumerable 而不是 ISet ,这乍看 上去会很奇怪,但却意味着集可以很自然地与LINQ进行交互。
B.4.1 HashSet
HashSet 是不含值的 Dictionary<,> 。它们具有相同的性能特征,并且你也可以指定一 个 IEqualityComparer 来自定义项的比较。同样, HashSet 所维护的顺序也不一定就是 值添加的顺序。
HashSet 添加了一个 RemoveWhere 方法,可以移除所有匹配给定谓词的条目。这可以在 迭代时对集进行删减,而不必担心在迭代时不能修改集合的禁令。
B.4.2 SortedSet (.NET 4)
就像 HashSet 之于 Dictionary<,> 一样, SortedSet 是没有值的 SortedDic- tionary<,> 。它维护一个值的红黑树,添加、移除和包含检查(containment check)的复杂度 为O(log n)。在对集进行迭代时,产生的是排序的值。
和 HashSet 一样它也提供了 RemoveWhere 方法(尽管接口中没有),并且还提供了额外 的属性( Min 和 Max )用来返回最小和最大值。一个比较有趣的方法是 GetViewBetween ,它返 回介于原始集上下限之内(含上下限)的另一个 SortedSet 。这是一个易变的活动视图—— 对于它的改变将反映到原始集上,反之亦然,如代码清单B-2所示。
var baseSet = new SortedSet<int> { 1, 5, 10, 15, 20 };
var view = baseSet.GetViewBetween(10, 20);
view.Add(14);
Console.WriteLine(baseSet.Count);
foreach (var item in view)
{
Console.WriteLine(item);
}
尽管 GetViewBetween 很方便,却不是免费的午餐:为保持内部的一致性,对视图的操作可 能比预期的更昂贵。尤其在访问视图的 Count 属性时,如果在上次遍历之后基础集发生了改变, 操作的复杂度将为O(n)。所有强大的工具,都应该谨慎用之。 SortedSet 的最后一个特性是它公开了一个 Reverse() 方法,可以进行反序迭代。 Enumerable.Reverse() 没有使用该方法,而是缓冲了它调用的序列的内容。如果你知道要反 序访问排序集,使用 SortedSet 类型的表达式代替更通用的接口类型可能会更有用,因为可 访问这个更高效的实现。