List<List<int>> Remove() method

4
我想在列表中使用Remove()方法,但是对于我来说它不起作用。
一个简单的例子可以说明一切:
List<List<int>> list = new List<List<int>>();
list.Add(new List<int> { 0, 1, 2 });
list.Add(new List<int> { 1, 2 });
list.Add(new List<int> { 4 });
list.Add(new List<int> { 0, 1, });

list.Remove(new List<int> { 1, 2 });

如果我使用RemoveAt(1),它可以正常工作,但是Remove()不行。
很明显这段代码返回false的原因是相同的:
List<int> l1 = new List<int>();
List<int> l2 = new List<int>();
l1.Add(1);
l2.Add(1);

bool b1 = l1 == l2; // returns False
bool b2 = l1.Equals(l2); // returns False too

所以我觉得我不能简单地比较两个列表或甚至数组。我可以使用循环代替Remove(),但一定有更简单的方法。

提前感谢。


你有没有考虑过创建自己的类,而不是创建一个类型为List的List? - Jamie Keeling
1
这不是一个答案,而是一个解释为什么它不能工作。List<T>.Remove()使用默认的相等比较器EqualityComparer<T>.Default来确定相等性。这只是一些额外的信息,可能会帮助其他人帮助你。 - Only Bolivian Here
@Jamie:如果你告诉他为什么在这种情况下创建自己的类会对他有所帮助,那将会很有帮助 :) - Only Bolivian Here
@Sergio 抱歉,@cozzy 你是在使用 List 对象的某些特性还是它只是方便使用? - Jamie Keeling
@Jamie Keeling:我选择使用列表而不是数组,因为我不需要在开始时设置数组的大小。除此之外,我没有考虑其他任何事情,也不知道如何使用我的新类或类型。 - cozzy
6个回答

11
问题在于List<T>没有重写EqualsGetHashCode,这是List<T>在查找项时会使用的方法。(实际上,它会使用默认的相等比较器,这意味着如果对象实现了IEquatable<T>,则会使用其实现,否则会回退到object.Equals/GetHashCode)。Equals将返回false,因为您正在尝试删除不同的对象,而默认实现只是比较引用。
基本上,您需要编写一个比较两个列表是否相等的方法,并使用该方法找到要删除的条目的索引。然后,您需要通过索引进行删除(使用RemoveAt)。 编辑:正如已指出的,可以使用Enumerable.SequenceEqual来比较列表。由于可以轻松计算它们的计数,但最初未检查计数是否相等,因此这并不是非常高效。另外,如果您仅需要比较List<int>值,则可以避免对相等比较器进行虚方法调用。
另一种选择是避免首先使用List<List<int>> - 使用包含List<int>List<SomeCustomType>。然后可以在该类型中实现IEquatable<T>。请注意,这也可能使您能够在自定义类型中封装适当的逻辑。我经常发现,通过类型嵌套集合类型,自定义类型更有效地封装了内部集合的含义。

@Jamie:是的,我们想法一致 :) - Jon Skeet
@Jon: 我泡了杯咖啡,坐下来为此工作了一个例子,我简直不敢相信我才刚刚了解到它。这是一个非常好的工具。 :D - Only Bolivian Here
@cozzy:然而,jCoder的答案确实非常易读,所以如果它能满足你的需求,那就是一个好的解决方案。 - Jon Skeet
@Jon:原始帖子没有提供有关要删除的序列是否会多次出现的信息,因此我认为删除所有这些序列应该是可以的。如果内部列表背后有更多含义,我会选择一个封装对象或至少一个自己的比较方法。当然,这个解决方案是可读的,但根据其上下文可以进行优化。 - jCoder
@jCoder:嗯,OP试图使用List<T>.Remove,它总是删除第一个出现的元素 - 所以我认为这是所需的。但是,是的,我们了解到的实际需求和语义含义(以及预期大小)越多,我们就能提供更多帮助。 - Jon Skeet
显示剩余3条评论

6

第一种方法:

List<int> listToRemove = new List<int> { 1, 2 };
list.RemoveAll(innerList => innerList.Except(listToRemove).Count() == 0);

这也会删除列表 { 2, 1 }

第二种方法(首选):

List<int> listToRemove = new List<int> { 1, 2 };
list.RemoveAll(innerList => innerList.SequenceEqual(listToRemove));

这将删除所有包含与提供的列表相同序列的列表。

不要误解其他答案中的建议,虽然它们都很有价值,但是人们认为OP尝试的方法“根本行不通”。 - Kimberly
看起来它可以做我想要的事情,而且也是最简单的方法。所以我可能会使用它,谢谢。 - cozzy

4

列表相等性是引用相等性。只有当它具有与外部列表中的列表相同的引用时,才不会删除列表。您可以创建一个新类型,将相等性实现为集合相等性而不是引用相等性(或者您也关心顺序吗?)。然后,您可以使用此类型的列表代替。


2
这根本行不通,因为你试图删除一个全新的列表(new关键字就是这样规定的),而不是你刚刚添加的列表之一。例如,以下代码创建了两个不同的列表,尽管它们看起来相同,但它们并不是同一个列表
var list0 = new List<int> { 1, 2 };

var list1 = new List<int> { 1, 2 };

然而,以下代码创建了一个列表,但是两个引用指向同一个列表:

var list0 = new List<int> { 1, 2 };

var list1 = list0;

因此,如果您希望在将来使用 Remove 对其进行操作,则应保留对其中列表的引用,例如:
var list0 = new List<int> { 1, 2 };

listOfLists.Remove(list0);

1

它们是不同的对象。尝试这样做:

  List<int> MyList =  new List<int> { 1, 2 };   

  List<List<int>> list = new List<List<int>>();
  list.Add(new List<int> { 0, 1, 2 });
  list.Add(MyList);
  list.Add(new List<int> { 4 });
  list.Add(new List<int> { 0, 1, });

  list.Remove(MyList);

0

你需要指定要删除的列表的引用:

list.Remove(list[1]);

实际上,这与

list.RemoveAt(1);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接