2010-10-09 26 views
8

我有一个HashSet,减去HashSets(并返回一份副本)?

var universe = new HashSet<int>(); 

和一堆的子集,

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

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

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

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

+0

你的意思是你想知道如何克隆哈希集合? – kennytm 2010-10-09 19:37:39

+0

@KennyTM:我的意思是我想知道如何完成工作。如果这意味着克隆,那么是的,如果有更好的方法,那么不。 – mpen 2010-10-09 19:46:24

回答

8

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

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

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

并不是这么简单,与Except扩展方法,但可能更快(你应该运行一些性能测试,以确保)

+0

不幸的是,你正在使用的['new HashSet (IEnumerable )'](https://msdn.microsoft.com/en-us/library/bb301504.aspx)并没有利用现有设置只包含不同的元素,并且为每个单独的项目调用昂贵的“添加(项目)”方法,而不是有效地对内部数据结构进行浅层克隆,即使用越来越大的'universe's,这会比它慢得多。因此:+1为您的后续问题:[有效的方法克隆HashSet ?](http://stackoverflow.com/q/3927789/709537) – 2015-07-03 02:10:25

9

Except()怎么样?

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

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

但'Except'是一个扩展方法,'ExceptWith'专门用于与'HashSets'一起工作...这是一样高效吗? – mpen 2010-10-09 19:47:19

+1

@Mark,它肯定比* ExceptWith'效率低,但它的效率与克隆它先有效率,然后调用ExceptWith一样。 – 2010-10-10 00:02:45

+1

@Kirk:终于开始测试了。不对。它仍然大约慢了40%。 http://programanddesign.com/cs/subtracting-sets/ – mpen 2010-12-31 05:08:05

1

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

+0

是的..你说得对,我不认为无论如何,我需要一个深层复制。在这个例子中使用int,但他们将在实践中成为类;一个参考是好的,但。 – mpen 2010-10-10 08:57:30

4

我对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毫秒
本机:762毫秒

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

0

很晚了答案,但有时可能是有用的。

@mpen除(IEnumerable的<>)

这使得LINQ环槽的IEnumerable检查它是否含有回答使用LINQ的。

如何

setA.Where(I =>!setB.Contains(I))