从列表中删除对象<当List包含相同的对象而不创建新列表时
本文关键字:列表 对象 新列表 创建 删除 List 包含相 | 更新日期: 2023-09-27 18:13:57
我有
var a = new object();
var b = new object();
var c = new object();
var d = new object();
var e = new object();
var list = new List<object> {a, b, c, d, e, a, d};
然后我需要删除最后两个对象(a
和d
),因为它们已经在列表中了。
在不创建新列表的情况下如何做到这一点?
如果你调用list.Remove
,它将删除第一个实例;但是,您可以使用list.RemoveAt
和list.RemoveRange
。例如:
list.RemoveRange(5,2);
或者更好:一开始就不要添加它们。
例如,如果你从一个序列中添加,你可以使用Distinct
或HashSet<T>
来查找添加时唯一的项。
事后,您可以使用:
var dups = from item in list
group item by item into grp
where grp.Count() > 1
select grp.Key;
foreach (var val in dups)
{
int first = list.IndexOf(val), last;
while ((last = list.LastIndexOf(val)) != first)
{
list.RemoveAt(last);
}
}
删除除第一个副本实例外的所有副本。
或者更有效的:
for (int i = 0; i < list.Count; i++ )
{
var val = list[i];
int first = list.IndexOf(val), last;
while ((last = list.LastIndexOf(val)) != first)
{
list.RemoveAt(last);
}
}
您可以使用这个反向for循环和Enumerable.Contains
+ List.RemoveAt
。这将不会创建一个新的列表:
var list = new List<object> { "a", "b", "c", "d", "e", "a", "d" };
for (int i = list.Count - 1; i >= 0; i--)
{
var obj = list[i];
if(list.Take(i).Contains(obj))
list.RemoveAt(i);
}
这个循环通过索引器获取一个对象,取所有在它的索引之前的对象并相互比较。请注意,Take
由于延迟执行而不创建集合,将其理解为一个循环,只要Contains
返回true
,它就会结束。如果它已经可用,它将从列表中删除。所以早起的鸟儿有虫吃。
然而,如果你想支持自定义类型,你需要在你的类中覆盖Equals
和GetHashCode
,否则Contains
只会比较引用。
如何在不创建新列表的情况下做到这一点?
您的要求有些令人惊讶(或者至少您没有解释为什么必须就地修改列表很重要),但是这里有一个有利于提高内存使用速度的解决方案:
var set = new HashSet<object>();
var indicesToRemove = new List<int>();
for (var i = 0; i < list.Count; ++i) {
var item = list[i];
if (!set.Contains(item))
set.Add(item);
else
indicesToRemove.Add(i);
}
var itemsRemovedSoFar = 0;
foreach (var i in indicesToRemove) {
list.RemoveAt(i - itemsRemovedSoFar);
itemsRemovedSoFar += 1;
}
与创建新列表的解决方案进行比较:
var list = list.Distinct().ToList();
我当然更喜欢第二个解决方案,但它不符合你的要求。