我有一个 HashSet 集合,
var universe = new HashSet<int>();
还有很多子集,
var sets = new List<HashSet<int>>(numSets);
我想要减去一个块,我可以像这样做:
var remaining = universe.ExceptWith(sets[0]);
ExceptWith
是就地操作的。我不想修改 universe
。我应该首先克隆它,还是有更好的方法?
我有一个 HashSet 集合,
var universe = new HashSet<int>();
还有很多子集,
var sets = new List<HashSet<int>>(numSets);
我想要减去一个块,我可以像这样做:
var remaining = universe.ExceptWith(sets[0]);
ExceptWith
是就地操作的。我不想修改 universe
。我应该首先克隆它,还是有更好的方法?
我猜我应该先克隆它?我该怎么做?
var universe = new HashSet<int>();
var subset = new HashSet<int>();
...
// clone the universe
var remaining = new HashSet<int>(universe);
remaining.ExceptWith(subset);
与 Except
扩展方法不一样简单,但可能更快(你应该运行一些性能测试来确保)
new HashSet<T>(IEnumerable<T>)
并没有利用现有集合仅包含不同元素这一事实,并为每个单独的元素调用昂贵的“Add(item)”方法,而不是有效地浅克隆内部数据结构。也就是说,在具有越来越大的
universe`的情况下,这比它本应该更慢。因此,对于您的后续问题Efficient way to clone a HashSet<T>?,我给出+1。 - Evgeniy Berezovsky那 Except()
呢?
var x = new HashSet<int>();
var y = new HashSet<int>();
var xminusy = new HashSet<int>(x.Except(y));
Except
是一个扩展方法,而ExceptWith
专门用于与HashSets
一起使用...这样做是否同样有效? - mpenExceptWith
不够高效,但它的效率与首先克隆它然后调用ExceptWith
大致相同。 - Kirk WollHashSet
的构造函数接受自定义的IEqualityComparer
。 - StriplingWarrior我对Linq的Except
方法进行了基准测试,与克隆并使用HashSet原生函数ExceptWith
进行了比较。以下是测试结果。
static class Program
{
public static HashSet<T> ToSet<T>(this IEnumerable<T> collection)
{
return new HashSet<T>(collection);
}
public static HashSet<T> Subtract<T>(this HashSet<T> set, IEnumerable<T> other)
{
var clone = set.ToSet();
clone.ExceptWith(other);
return clone;
}
static void Main(string[] args)
{
var A = new HashSet<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var B = new HashSet<int> { 2, 4, 6, 8, 10 };
var sw = new Stopwatch();
sw.Restart();
for (int i = 0; i < 1000000; ++i)
{
var C = A.Except(B).ToSet();
}
sw.Stop();
Console.WriteLine("Linq: {0} ms", sw.ElapsedMilliseconds);
sw.Restart();
for (int i = 0; i < 1000000; ++i)
{
var C = A.Subtract(B);
}
sw.Stop();
Console.WriteLine("Native: {0} ms", sw.ElapsedMilliseconds);
Console.ReadLine();
}
}
Linq: 1297毫秒
Native: 762毫秒
回答虽晚,但有时可能会有用。
@mpen使用Linq的Except(IEnumerable<>)进行了回答。
这使得Linq循环遍历IEnumerable并检查其是否包含。
那么如何处理呢?
setA.Where(i => !setB.Contains(i))
显然,在某些情况下,手动在循环中添加项目比复制整个集合然后删除项目更有效率。我能想到的一个例子是...
// no more set ops planned? then returning list is an option
public static List<T> ExceptWith<T>(HashSet<T> allObjects, Hashset<T> minus)
{
// Set Capacity of list (allObjects.Count-minus.Count?)
List<T> retlst = new List<T>(allObjects.Count);
foreach( var obj in allObjects) {
if( minus.Contains(obj)==false)
retlst.Add(obj);
}
return retlst;
}
// Special case where quantity of copying will be high
// more expensive in fact than just adding
public static HashSet<T> ExceptWith<T>(HashSet<T> allObjects, HashSet<T> minus)
{
if( minus.Count > allObjects.Count * 7/8 )
{
HashSet<T> retHash = new HashSet<T>();
foreach( var obj in allObjects) {
if( minus.Contains(obj)==false)
retHash.Add(obj);
}
return retHash;
}
else
{
// usual clone and remove
}
}