sortedset底层基于红黑树,插入即有序去重,add()后遍历天然升序;不支持索引访问,依赖icomparer或icomparable排序,频繁增删查且需动态有序时适用。

SortedSet
为什么 Add() 后遍历就是有序的?
SortedSetAdd() 都按 IComparer<t></t> 或 IComparable<t></t> 定义的顺序插入并自动平衡。这意味着:
- 插入
5, 1, 3后调用foreach,得到的是1, 3, 5,不是插入顺序 - 重复添加
3不会报错,也不会改变集合——Add()返回false表示未新增 - 没有索引访问(
set[0]会编译失败),只能用First()、Last()或枚举
自定义排序:IComparer vs 实现 IComparable
默认只支持 int、string 等内置类型;对自定义类,必须显式提供比较逻辑。两种方式效果等价,但适用场景不同:
- 用
IComparer<t></t>:适合临时、多规则排序,比如按价格升序、按名称降序切换——传入new PriceComparer()或StringComparer.OrdinalIgnoreCase - 实现
IComparable<t></t>:适合类型本身有“天然顺序”,比如Person按身份证号排序,且该规则稳定不变 - 注意:如果同时提供了
IComparer<t></t>构造参数,它会覆盖类型自身的IComparable<t></t>实现
示例(按长度排序字符串):
var set = new SortedSet<string>(StringComparer.Ordinal); // 错!这是字典序<br>var set = new SortedSet<string>(Comparer<string>.Create((a, b) => a.Length.CompareTo(b.Length)));</string></string></string>
SortedSet 和 HashSet / List 的关键区别
选错类型会导致性能或语义错误:
-
HashSet<t></t>:去重快(O(1) 平均),但不保证顺序;SortedSet<t></t>插入/查找是 O(log n),空间占用更大(每个元素带左右子节点引用) -
List<t></t>+Distinct().OrderBy():适合一次性处理、后续只读;而SortedSet<t></t>适合频繁增删查且始终需要有序视图 - 不能用
SortedSet<t></t>存可变对象(如未重写GetHashCode和Equals的 class),否则修改后可能破坏树结构,导致Contains()失效或遍历跳项
容易被忽略的边界行为
这些细节常在调试时暴露:
-
RemoveWhere()是 O(n log n),不是 O(n) —— 因为每删一个都要重新平衡树 -
UnionWith()、IntersectWith()等批量操作,传入的集合类型不影响结果顺序,但若传入未排序的List<t></t>,内部仍会逐个Add(),不会“批量建树” -
null值:引用类型 T 可为null,但只有当比较器允许(如Comparer<string>.Default</string>支持)才安全;用StringComparer.Ordinal时null会被排在最前 - 不要依赖
SortedSet<t></t>的枚举顺序做“第 N 小”查询——没有ElementAt(n),要取中位数得先转ToList(),这会丢掉 O(log n) 优势
真正需要动态维护有序+去重集合时,SortedSet<t></t> 是少数几个不需自己手写平衡树的选项;但只要数据量小、变更少,或者只需要最终有序,HashSet<t></t> 加一次 OrderBy 往往更轻量、更直观。










