从列表中删除对象<当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};

然后我需要删除最后两个对象(ad),因为它们已经在列表中了。
在不创建新列表的情况下如何做到这一点?

从列表中删除对象<当List包含相同的对象而不创建新列表时

如果你调用list.Remove,它将删除第一个实例;但是,您可以使用list.RemoveAtlist.RemoveRange。例如:

list.RemoveRange(5,2);

或者更好:一开始就不要添加它们

例如,如果你从一个序列中添加,你可以使用DistinctHashSet<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,它就会结束。如果它已经可用,它将从列表中删除。所以早起的鸟儿有虫吃。

然而,如果你想支持自定义类型,你需要在你的类中覆盖EqualsGetHashCode,否则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();

我当然更喜欢第二个解决方案,但它不符合你的要求。