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