在知道项目后,在C#中从集合中删除项目的最佳方法是什么,但不是它的索引。这是一种方法,但它看起来似乎不够优雅。
//Remove the existing role assignment for the user.
int cnt = 0;
int assToDelete = 0;
foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
{
if (spAssignment.Member.Name == shortName)
{
assToDelete = cnt;
}
cnt++;
}
workspace.RoleAssignments.Remove(assToDelete);
我真正想做的是找到要按属性删除的项目(在本例中为name),而不循环遍历整个集合并使用另外两个变量。
答案 0 :(得分:127)
如果RoleAssignments是List<T>
,您可以使用以下代码。
workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);
答案 1 :(得分:25)
如果您想通过其中一个属性访问该集合的成员,则可以考虑使用Dictionary<T>
或KeyedCollection<T>
。这样您就不必搜索您要查找的项目。
否则,你至少可以这样做:
foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
{
if (spAssignment.Member.Name == shortName)
{
workspace.RoleAssignments.Remove(spAssignment);
break;
}
}
答案 2 :(得分:21)
@smaclell问为什么反向迭代在对@ sambo99的评论中效率更高。
有时它更有效率。假设您有一个人员列表,并且您想删除或过滤所有信用评级为&lt; 1000;
我们有以下数据
"Bob" 999
"Mary" 999
"Ted" 1000
如果我们要向前推进,我们很快就会遇到麻烦
for( int idx = 0; idx < list.Count ; idx++ )
{
if( list[idx].Rating < 1000 )
{
list.RemoveAt(idx); // whoops!
}
}
在idx = 0时,我们删除Bob
,然后将所有剩余的元素移开。下一次通过循环idx = 1,但是
list [1]现在是Ted
而不是Mary
。我们最终错误地跳过了Mary
。我们可以使用while循环,我们可以引入更多变量。
或者,我们只是反向迭代:
for (int idx = list.Count-1; idx >= 0; idx--)
{
if (list[idx].Rating < 1000)
{
list.RemoveAt(idx);
}
}
删除项目左侧的所有索引都保持不变,因此您不会跳过任何项目。
如果给出要从数组中删除的索引列表,则同样的原则适用。为了保持正确,您需要对列表进行排序,然后将项目从最高索引删除到最低。
现在你可以使用Linq并以直截了当的方式声明你正在做的事情。
list.RemoveAll(o => o.Rating < 1000);
对于删除单个项目的情况,向前或向后迭代不再有效。您也可以使用Linq。
int removeIndex = list.FindIndex(o => o.Name == "Ted");
if( removeIndex != -1 )
{
list.RemoveAt(removeIndex);
}
答案 3 :(得分:9)
对于简单的List结构,最有效的方法似乎是使用Predicate RemoveAll实现。
例如
workSpace.RoleAssignments.RemoveAll(x =>x.Member.Name == shortName);
原因是:
如果您在前c#3.0时代遇到困难。你有2个选择。
EG。
List<int> list2 = new List<int>() ;
foreach (int i in GetList())
{
if (!(i % 2 == 0))
{
list2.Add(i);
}
}
list2 = list2;
或
如果要从列表中经常删除内容,可能是另一种结构,如HashTable(。net 1.1)或Dictionary(。net 2.0)或HashSet(。净3.5)更适合此目的。
答案 4 :(得分:9)
如果是ICollection
,则您将不会使用RemoveAll
方法。这是一个扩展方法:
public static void RemoveAll<T>(this ICollection<T> source,
Func<T, bool> predicate)
{
if (source == null)
throw new ArgumentNullException("source", "source is null.");
if (predicate == null)
throw new ArgumentNullException("predicate", "predicate is null.");
source.Where(predicate).ToList().ForEach(e => source.Remove(e));
}
基于: http://phejndorf.wordpress.com/2011/03/09/a-removeall-extension-for-the-collection-class/
答案 5 :(得分:7)
该系列是什么类型的?如果是List,您可以使用有用的“RemoveAll”:
int cnt = workspace.RoleAssignments
.RemoveAll(spa => spa.Member.Name == shortName)
(这适用于.NET 2.0。当然,如果你没有更新的编译器,你将不得不使用“delegate(SPRoleAssignment spa){return spa.Member.Name == shortName;}”来代替lambda语法很好。)
另一种方法,如果它不是List,但仍然是ICollection:
var toRemove = workspace.RoleAssignments
.FirstOrDefault(spa => spa.Member.Name == shortName)
if (toRemove != null) workspace.RoleAssignments.Remove(toRemove);
这需要Enumerable扩展方法。 (如果您遇到.NET 2.0,可以复制Mono)。如果它是一些无法获取项目的自定义集合,但必须采用索引,则其他一些Enumerable方法(如Select)会为您传入整数索引。
答案 6 :(得分:2)
这是一个非常好的方法
http://support.microsoft.com/kb/555972
System.Collections.ArrayList arr = new System.Collections.ArrayList();
arr.Add("1");
arr.Add("2");
arr.Add("3");
/*This throws an exception
foreach (string s in arr)
{
arr.Remove(s);
}
*/
//where as this works correctly
Console.WriteLine(arr.Count);
foreach (string s in new System.Collections.ArrayList(arr))
{
arr.Remove(s);
}
Console.WriteLine(arr.Count);
Console.ReadKey();
答案 7 :(得分:2)
这是我的通用解决方案
public static IEnumerable<T> Remove<T>(this IEnumerable<T> items, Func<T, bool> match)
{
var list = items.ToList();
for (int idx = 0; idx < list.Count(); idx++)
{
if (match(list[idx]))
{
list.RemoveAt(idx);
idx--; // the list is 1 item shorter
}
}
return list.AsEnumerable();
}
如果扩展方法支持通过引用传递,那么看起来会更简单! 用法:
var result = string[]{"mike", "john", "ali"}
result = result.Remove(x => x.Username == "mike").ToArray();
Assert.IsTrue(result.Length == 2);
编辑:确保即使通过递减索引(idx)删除项目,列表循环仍然有效。
答案 8 :(得分:0)
根据您使用收藏的方式,您可以采取另一种方法。如果您正在下载作业一次(例如,当应用程序运行时),您可以动态地将集合翻译成哈希表,其中:
shortname =&gt; SPRoleAssignment
如果您这样做,那么当您想要通过短名称删除项目时,您需要做的就是按键从哈希表中删除该项目。
不幸的是,如果您正在加载这些SPRoleAssignments,那么就时间而言,这显然不会更具成本效益。如果你使用新版本的.NET Framework,那么其他人对使用Linq的建议会很好,但除此之外,你必须坚持使用你正在使用的方法。
答案 9 :(得分:0)
这里有很多好的回应;我特别喜欢lambda表达式......非常干净。但是,我没有指定Collection的类型。这是一个SPRoleAssignmentCollection(来自MOSS),只有Remove(int)和Remove(SPPrincipal),而不是方便的RemoveAll()。所以,我已经解决了这个问题,除非有更好的建议。
foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments)
{
if (spAssignment.Member.Name != shortName) continue;
workspace.RoleAssignments.Remove((SPPrincipal)spAssignment.Member);
break;
}
答案 10 :(得分:0)
要在循环遍历集合时执行此操作而不是修改集合异常,这是我过去采用的方法(注意原始集合末尾的.ToList(),这会创建另一个在内存中收集,然后你可以修改现有的集合)
foreach (SPRoleAssignment spAssignment in workspace.RoleAssignments.ToList())
{
if (spAssignment.Member.Name == shortName)
{
workspace.RoleAssignments.Remove(spAssignment);
}
}
答案 11 :(得分:0)
与Dictionary Collection相似,我已经做到了。
Dictionary<string, bool> sourceDict = new Dictionary<string, bool>();
sourceDict.Add("Sai", true);
sourceDict.Add("Sri", false);
sourceDict.Add("SaiSri", true);
sourceDict.Add("SaiSriMahi", true);
var itemsToDelete = sourceDict.Where(DictItem => DictItem.Value == false);
foreach (var item in itemsToDelete)
{
sourceDict.Remove(item.Key);
}
注意:强> 上面的代码将在.Net Client Profile(3.5和4.5)中失败,也有一些观众提到它 在.Net4.0中失败了,不知道哪个设置导致问题。
所以用Where语句替换下面的代码(.ToList()),以避免该错误。 “收集被修改;枚举操作可能无法执行。“
var itemsToDelete = sourceDict.Where(DictItem => DictItem.Value == false).ToList();
每个MSDN从.Net4.5开始,客户端配置文件已停止。 http://msdn.microsoft.com/en-us/library/cc656912(v=vs.110).aspx
答案 12 :(得分:0)
首先保存您的项目,而不是删除它们。
var itemsToDelete = Items.Where(x => !!!your condition!!!).ToArray();
for (int i = 0; i < itemsToDelete.Length; ++i)
Items.Remove(itemsToDelete[i]);
您需要覆盖Item类中的GetHashCode()
。
答案 13 :(得分:0)
最好的方法是使用linq。
示例类:
public class Product
{
public string Name { get; set; }
public string Price { get; set; }
}
Linq查询:
var subCollection = collection1.RemoveAll(w => collection2.Any(q => q.Name == w.Name));
如果collection1
与Name
Name
匹配,此查询将从collection2
中移除所有元素
请记住使用:using System.Linq;
答案 14 :(得分:0)
如果您有List<T>
,那么List<T>.RemoveAll
是您最好的选择。再没有比这更有效的了。在内部它使阵列移动一次,更不用说是O(N)。
如果您只拥有IList<T>
或ICollection<T>
,则大致可以选择以下三个选项:
public static void RemoveAll<T>(this IList<T> ilist, Predicate<T> predicate) // O(N^2)
{
for (var index = ilist.Count - 1; index >= 0; index--)
{
var item = ilist[index];
if (predicate(item))
{
ilist.RemoveAt(index);
}
}
}
或
public static void RemoveAll<T>(this ICollection<T> icollection, Predicate<T> predicate) // O(N)
{
var nonMatchingItems = new List<T>();
// Move all the items that do not match to another collection.
foreach (var item in icollection)
{
if (!predicate(item))
{
nonMatchingItems.Add(item);
}
}
// Clear the collection and then copy back the non-matched items.
icollection.Clear();
foreach (var item in nonMatchingItems)
{
icollection.Add(item);
}
}
或
public static void RemoveAll<T>(this ICollection<T> icollection, Func<T, bool> predicate) // O(N^2)
{
foreach (var item in icollection.Where(predicate).ToList())
{
icollection.Remove(item);
}
}
选择1或2。
1的内存较轻,如果要执行的删除操作较少(例如,谓词通常为假),则速度更快。
如果要执行更多删除操作,则2更快。
3是最干净的代码,但IMO表现不佳。同样,所有这些都取决于输入数据。
有关某些基准测试的详细信息,请参见https://github.com/dotnet/BenchmarkDotNet/issues/1505