减去HashSets(并返回副本)?

时间:2010-10-09 19:33:59

标签: c# hashset

我有一个HashSet,

var universe = new HashSet<int>();

还有一堆子集,

var sets = new List<HashSet<int>>(numSets);

我想减去一个块,我可以这样做:

var remaining = universe.ExceptWith(sets[0]);

但是ExceptWith就地工作。我不想修改universe。我应该先克隆它,还是有更好的方法?

6 个答案:

答案 0 :(得分:13)

  

我想我应该克隆它   第一?我该怎么做?

var universe = new HashSet<int>();
var subset = new HashSet<int>();
...

// clone the universe
var remaining = new HashSet<int>(universe);
remaining.ExceptWith(subset);

不像Except扩展方法那么简单,但可能更快(你应该运行一些性能测试来确保)

答案 1 :(得分:10)

Except()怎么样?

var x = new HashSet<int>();
var y = new HashSet<int>();

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

答案 2 :(得分:8)

我对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 ms
  原生:762毫秒

http://programanddesign.com/cs/subtracting-sets/

答案 3 :(得分:1)

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

答案 4 :(得分:0)

答案非常晚,但有时可能很有用。

@mpen使用Linq的Except(IEnumerable&lt;&gt;)

回答

使linq循环通过IEnumerable检查它是否包含。

怎么样

  

setA.Where(i =&gt;!setB.Contains(i))

答案 5 :(得分:0)

很明显,在某些情况下,“手动”循环添加项目比复制整个集合然后删除项目更有效。我能想到的一个...

// 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
    }
}