C# 从集合中查找和删除项目
从集合中删除集合,但仍保留在单独集合中删除的项目的最佳方法是什么 我已经写了一个扩展方法来实现这一点,但我认为一定有更好的方法。以下是我的功能:C# 从集合中查找和删除项目,c#,linq,C#,Linq,从集合中删除集合,但仍保留在单独集合中删除的项目的最佳方法是什么 我已经写了一个扩展方法来实现这一点,但我认为一定有更好的方法。以下是我的功能: public static List<T> FindAndRemove<T>(this List<T> lst, Predicate<T> match) { List<T> ret = lst.FindAll(match); lst.RemoveAll(match);
public static List<T> FindAndRemove<T>(this List<T> lst, Predicate<T> match)
{
List<T> ret = lst.FindAll(match);
lst.RemoveAll(match);
return ret;
}
公共静态列表FindAndRemove(此列表lst,谓词匹配)
{
列表ret=lst.FindAll(匹配);
1.移除所有(匹配);
返回ret;
}
你可以这样使用它:
List<String> myList = new List<String>();
myList.Add("ABC");
myList.Add("DEF");
myList.Add("ABC");
List<String> removed = myList.FindAndRemove(x => x == "ABC");
// myList now contains 1 item (DEF)
// removed now contains 2 items (ABC, ABC)
var ret = new List<T>();
var remaining = new List<T>();
foreach (T t in lst) {
if (match(t))
{
ret.Add(t);
}
else
{
remaining.Add(t);
}
}
lst.Clear();
lst.AddRange(remaining);
return ret;
List myList=new List();
myList.Add(“ABC”);
myList.添加(“定义”);
myList.Add(“ABC”);
移除列表=myList.FindAndRemove(x=>x==“ABC”);
//myList现在包含1项(DEF)
//已删除现在包含2项(ABC,ABC)
我不能100%确定
FindAll
和RemoveAll
方法的幕后活动,但我认为更好的方法是以某种方式将项目从一个列表“转移”到另一个列表。我不认为这是最有效的方法-您在列表的每个元素上调用谓词match
我会这样做:
List<String> myList = new List<String>();
myList.Add("ABC");
myList.Add("DEF");
myList.Add("ABC");
List<String> removed = myList.FindAndRemove(x => x == "ABC");
// myList now contains 1 item (DEF)
// removed now contains 2 items (ABC, ABC)
var ret = new List<T>();
var remaining = new List<T>();
foreach (T t in lst) {
if (match(t))
{
ret.Add(t);
}
else
{
remaining.Add(t);
}
}
lst.Clear();
lst.AddRange(remaining);
return ret;
var ret=newlist();
var剩余=新列表();
foreach(第一层中的T){
if(匹配(t))
{
再加上(t);
}
其他的
{
剩余。添加(t);
}
}
lst.Clear();
一级添加范围(剩余);
返回ret;
根据集合的大小,您可能希望将其实现为哈希集而不是列表。在足够大的集合中(根据我的经验,“足够”的大小多少在某种程度上取决于集合中的内容),哈希集在查找自身中的项时可以比列表快得多 Op的答案是迄今为止提出和建议的解决方案中最好的。以下是我的机器上的计时:
public static class Class1
{
// 21ms on my machine
public static List<T> FindAndRemove<T>(this List<T> lst, Predicate<T> match)
{
List<T> ret = lst.FindAll(match);
lst.RemoveAll(match);
return ret;
}
// 538ms on my machine
public static List<T> MimoAnswer<T>(this List<T> lst, Predicate<T> match)
{
var ret = new List<T>();
int i = 0;
while (i < lst.Count)
{
T t = lst[i];
if (!match(t))
{
i++;
}
else
{
lst.RemoveAt(i);
ret.Add(t);
}
}
return ret;
}
// 40ms on my machine
public static IEnumerable<T> GuvanteSuggestion<T>(this IList<T> list, Func<T, bool> predicate)
{
var removals = new List<Action>();
foreach (T item in list.Where(predicate))
{
T copy = item;
yield return copy;
removals.Add(() => list.Remove(copy));
}
// this hides the cost of processing though the work is still expensive
Task.Factory.StartNew(() => Parallel.ForEach(removals, remove => remove()));
}
}
[TestFixture]
public class Tester : PerformanceTester
{
[Test]
public void Test()
{
List<int> ints = Enumerable.Range(1, 100000).ToList();
IEnumerable<int> enumerable = ints.GuvanteSuggestion(i => i % 2 == 0);
Assert.That(enumerable.Count(), Is.EqualTo(50000));
}
}
公共静态类Class1
{
//21毫秒在我的机器上
公共静态列表FindAndRemove(此列表lst,谓词匹配)
{
列表ret=lst.FindAll(匹配);
1.移除所有(匹配);
返回ret;
}
//538ms在我的机器上
公共静态列表MimoAnswer(此列表lst,谓词匹配)
{
var ret=新列表();
int i=0;
而(i<1次计数)
{
T=lst[i];
如果(!匹配(t))
{
i++;
}
其他的
{
一、搬迁(一);
再加上(t);
}
}
返回ret;
}
//40毫秒在我的机器上
公共静态IEnumerable GuvanteSuggestion(此IList列表,Func谓词)
{
var removals=新列表();
foreach(列表中的T项,其中(谓词))
{
T拷贝=项目;
交回副本;
removals.Add(()=>list.Remove(copy));
}
//这隐藏了处理的成本,尽管这项工作仍然很昂贵
Task.Factory.StartNew(()=>Parallel.ForEach(removals,remove=>remove());
}
}
[测试夹具]
公共类测试员:PerformanceTester
{
[测试]
公开无效测试()
{
List ints=Enumerable.Range(11000.ToList();
IEnumerable enumerable=ints.GuvanteSuggestion(i=>i%2==0);
Assert.That(enumerable.Count(),Is.EqualTo(50000));
}
}
您应该尝试将原始列表划分为两个新列表。实现应该在任何IEnumerable上工作,而不仅仅是列表,并且应该假设源是不可变的。
请参阅关于分区的这篇文章:
.
我想已经涵盖了。您的解决方案是最有效的。您的实施在我看来很好。复制在.Net中是一种廉价的操作,因此没有理由进行“传输”(除非您需要某种线程/异常安全,即一个对象一次不在多个集合中),我同意。使用内置LINQ的目的是让生活更轻松。。场景后面发生的事情将是MS选择的最佳解决方案。因为您使用的是C#,这对眼睛来说很好,但VB会将您的查询以菊花链的形式链接起来。
return=lst.FindAll(match)。RemoveAll(match)
,以与读取代码样式保持一致。@ppumkin尽管不需要自定义扩展方法。您只需使用.Where()并使其不等于所需的值,然后列出它和BAM。在列表中迭代一次,并做最少的工作以获得所需的结果。什么是不喜欢的?@ppumkin:另一种方法是创建一个列表来保存找到的结果,然后在其中进行迭代,因为你不能在foreach中对列表进行变异。@Neil Moss-我从来没有说过这是错的。。我试图暗示它是旧的。。对但大多数情况下,它不是.NET结构。仅此而已:)-PS看起来像JAVA或C。在任何MS.NET考试中,这个答案都是错误的。。不是我的错。多亏DPedan检查了我之前提出的解决方案的执行时间-由于使用了RemoveAt(),所以执行速度非常慢-现在我修复了它。当谓词快时,上面的新解决方案与问题中的原始解决方案一样快,而当谓词慢时,新解决方案的速度大约是原始解决方案的两倍,因此它的执行控制着整个执行时间。不适用HashSet
在Contains
处速度很快。也就是说,查找指定项是否在集合中。问题是迭代集合,查找与谓词匹配的成员;这样做不涉及使用Contains
。