C# 减去哈希集(并返回一个副本)?

C# 减去哈希集(并返回一个副本)?,c#,hashset,C#,Hashset,我有一个哈希集 var universe = new HashSet<int>(); 但是,ExceptWith可以正常工作。我不想修改宇宙。我应该先克隆它,还是有更好的方法?除了()之外的怎么样 var x=newhashset(); var y=新的HashSet(); var xminusy=新的哈希集(x.除了(y)); 我想我应该克隆它 第一?我该怎么做 var universe=newhashset(); var子集=新的HashSet(); ... //克隆宇宙 v

我有一个哈希集

var universe = new HashSet<int>();
但是,
ExceptWith
可以正常工作。我不想修改
宇宙
。我应该先克隆它,还是有更好的方法?

除了()之外的
怎么样

var x=newhashset();
var y=新的HashSet();
var xminusy=新的哈希集(x.除了(y));
我想我应该克隆它 第一?我该怎么做

var universe=newhashset();
var子集=新的HashSet();
...
//克隆宇宙
var剩余=新哈希集(宇宙);
剩余。除(子集)外;

除了
扩展方法之外,没有
扩展方法那么简单,但可能更快(您应该运行一些性能测试以确保)

哈希集必须跟踪其哈希算法常量和溢出容器。集合中的元素通过引用保存。正如Thomas Levesque所建议的那样,使用复制构造函数创建一个新的散列会创建此开销的浅拷贝,并且应该非常快。按照James McNellis建议的方式使用Except()首先创建匿名副本,然后将其传递给复制构造函数,该构造函数使用匿名中的字段初始化自己的字段。正如托马斯所说,你可以做一些性能测试,但理论上他的答案应该优于詹姆斯的答案。顺便说一句,在我看来,浅拷贝不是克隆,因为我相信克隆意味着底层元素也被复制了。具有公共元素的哈希集在修改策略时使用副本。

我对Linq的
Except
方法进行了基准测试,以防止克隆和使用哈希集本机函数
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();
    }
}
静态类程序
{
公共静态HashSet-ToSet(此IEnumerable集合)
{
返回新的哈希集(集合);
}
公共静态哈希集减法(此哈希集,IEnumerable其他)
{
var clone=set.ToSet();
克隆。除(其他)外;
返回克隆;
}
静态void Main(字符串[]参数)
{
var A=新的哈希集{1,2,3,4,5,6,7,8,9,10};
var B=新的哈希集{2,4,6,8,10};
var sw=新秒表();
sw.Restart();
对于(int i=0;i<1000000;++i)
{
var C=A.除了(B).ToSet();
}
sw.Stop();
WriteLine(“Linq:{0}ms”,sw.elapsedmillesons);
sw.Restart();
对于(int i=0;i<1000000;++i)
{
var C=A减去(B);
}
sw.Stop();
WriteLine(“本机:{0}ms”,sw.elapsedmillesons);
Console.ReadLine();
}
}
Linq:1297毫秒
本地:762毫秒


回答得很晚,但有时可能有用

@使用Linq的EXPECT(IEnumerable)回答mpen

这使得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
    }
}
//是否不再计划设置操作?然后返回列表是一个选项
公共静态列表ExceptWith(哈希集AllObject,哈希集减号)
{
//设置列表的容量(AllObject.Count减去.Count?)
List retlst=新列表(allObjects.Count);
foreach(AllObject中的var obj){
if(减去包含(obj)=false)
更新添加(obj);
}
返回retlst;
}
//复印量大的特殊情况
//事实上,这比仅仅添加
公共静态哈希集ExceptWith(哈希集AllObject,哈希集减号)
{
如果(减.Count>allObjects.Count*7/8)
{
HashSet retHash=新HashSet();
foreach(AllObject中的var obj){
if(减去包含(obj)=false)
retHash.Add(obj);
}
返回retHash;
}
其他的
{
//常见的克隆和删除
}
}

你的意思是想知道如何克隆哈希集?@KennyTM:我的意思是想知道如何完成这项工作。如果这意味着克隆,那么是的,如果有更好的方法,那么不是。但是
Except
是一种扩展方法,
ExceptWith
是专门为使用
哈希集而构建的。。。这也一样有效吗?@Mark,它的效率肯定比不上
ExceptWith
,但它的效率与先克隆它然后调用
ExceptWith
@Kirk:终于开始测试了。不是真的。仍然要慢40%左右@拉尔夫:很有趣。这就是为什么我通常坚持回答C++问题;Except在需要IEqualityComparer的场景中更优越(假设使用两组序列化对象,其中对象的内容相同,但其哈希代码不同),不幸的是ExceptWith不支持自定义IEqualityComparer,但Except扩展方法支持。对于像这里这样的整数,这不是一个问题。。你说得对,反正我也不需要深度拷贝。在本例中使用int,但实际上它们是类;不过,引用是可以的。不幸的是,您使用的没有利用现有集合只包含不同元素的事实,并为每个单个项调用昂贵的“Add(item)”方法,而没有有效地浅层克隆内部数据结构,即随着
范围的不断扩大
s,这比可能的要慢得多。因此,您的后续问题为:+1:
var x = new HashSet<int>();
var y = new HashSet<int>();

var xminusy = new HashSet<int>(x.Except(y));
var universe = new HashSet<int>();
var subset = new HashSet<int>();
...

// clone the universe
var remaining = new HashSet<int>(universe);
remaining.ExceptWith(subset);
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();
    }
}
// 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
    }
}