如何从SortedSet<Itemset>中移除已添加至ItemsetCollection2的元素?
看起来你现在需要实现的是把ItemsetCollection1里的Itemset(也就是SortedSet<int>)迁移到ItemsetCollection2后,从原集合中移除这些元素的逻辑。这里有几个关键细节需要注意,我结合你的场景给出具体的解决方案:
核心问题分析
首先要明确:SortedSet<T>是引用类型,默认的相等判断是引用相等——只有当两个Itemset是同一个对象实例时,Contains方法才会返回true。如果你的场景中,添加到ItemsetCollection2的是原集合的引用,那直接用RemoveWhere没问题;但如果是复制内容生成的新Itemset,默认逻辑就会失效,这时候需要自定义相等比较器。
场景1:迁移的是原集合的引用
如果你的代码是直接把ItemsetCollection1中的元素引用添加到ItemsetCollection2,可以直接用RemoveWhere,但要注意不能在遍历原集合的同时修改它(会触发枚举器异常),正确的流程是:
// 假设两个集合的定义如下 SortedSet<SortedSet<int>> ItemsetCollection1 = new SortedSet<SortedSet<int>>(); SortedSet<SortedSet<int>> ItemsetCollection2 = new SortedSet<SortedSet<int>>(); // 先完成所有元素的迁移 foreach (var itemset in ItemsetCollection1) { ItemsetCollection2.Add(itemset); } // 再从原集合移除所有已迁移的元素 ItemsetCollection1.RemoveWhere(itemset => ItemsetCollection2.Contains(itemset));
这种方式简单直接,因为Contains判断的是引用是否一致,效率也比较高。
场景2:迁移的是内容相同的新实例
如果你的逻辑是复制Itemset的内容(比如新建SortedSet<int>来存储相同元素),这时候默认的引用相等判断就不生效了。你需要自定义一个相等比较器,让集合按照内容是否一致来判断元素相等:
第一步:定义自定义相等比较器
public class ItemsetEqualityComparer : IEqualityComparer<SortedSet<int>> { public bool Equals(SortedSet<int> x, SortedSet<int> y) { if (x == null && y == null) return true; if (x == null || y == null) return false; // 用SortedSet自带的SetEquals方法判断内容是否完全一致 return x.SetEquals(y); } public int GetHashCode(SortedSet<int> obj) { if (obj == null) return 0; // 生成基于集合内容的哈希码(保证相同内容的哈希值一致) int hash = 17; foreach (int num in obj) { hash = hash * 31 + num.GetHashCode(); } return hash; } }
第二步:初始化集合时传入比较器
// 创建集合时指定自定义比较器,确保相等判断基于内容 var ItemsetCollection1 = new SortedSet<SortedSet<int>>(new ItemsetEqualityComparer()); var ItemsetCollection2 = new SortedSet<SortedSet<int>>(new ItemsetEqualityComparer()); // 示例元素 var i = new SortedSet<int> {1, 2}; var j = new SortedSet<int> {2}; ItemsetCollection1.Add(i); ItemsetCollection1.Add(j); // 迁移内容(新建实例) foreach (var itemset in ItemsetCollection1) { ItemsetCollection2.Add(new SortedSet<int>(itemset)); } // 此时RemoveWhere可以正确识别内容相同的元素 ItemsetCollection1.RemoveWhere(itemset => ItemsetCollection2.Contains(itemset));
更高效的批量移除方式
如果原集合元素较多,RemoveWhere里每次调用Contains会带来额外的性能开销(O(log n)一次)。你可以先收集所有要移除的元素,再批量移除:
var toRemove = new List<SortedSet<int>>(); foreach (var itemset in ItemsetCollection1) { ItemsetCollection2.Add(itemset); toRemove.Add(itemset); } // 批量移除 foreach (var itemset in toRemove) { ItemsetCollection1.Remove(itemset); }
这种方式避免了重复的Contains查询,性能更好,同时也规避了枚举时修改集合的风险。
内容的提问来源于stack exchange,提问作者ABB

