我正在尝试从System.Collections.Generic.LinkedList中删除一个节点,其中T是具有多个属性的对象。我想根据匹配其中一个属性来删除节点,例如 T.paint.color = "蓝色"。起初我尝试过:
foreach (Car carNode in carList)
{
if (carNode.paint.color == "blue")
{
carList.Remove(carNode);
}
}
当然,这失败了,并显示"枚举器实例化后修改了集合"错误。MSDN 上的示例是一个简单的字符串数组,使用如下内容:
sentence.Remove("old");
我的问题是我如何(或是否)使用类似的东西(使用伪代码):
carList.Remove(the node where carList.paint.color == "blue");
谢谢。
所以这里有两种选择。 最容易编码但效果最差的选项是只抓取所有要删除的项目,然后在找到它们后将它们全部删除:
var carsToRemove = carList.Where(carNode => carNode.paint.color == "blue")
.ToList();
foreach(var car in carsToRemove)
carList.Remove(car);
请注意,ToList
调用在这里非常重要;Where
不允许推迟基础列表的迭代,否则将得到相同的并发修改错误。
这里有两个问题。 首先,您需要在内存中保存所有要删除的项目。 除非你有很多(我的意思是很多),否则还不错。 更成问题的是,你没有节点对象,你有节点的值,所以你需要从一开始就遍历整个列表,找到每个对象并删除它们。 您已将 O(n) 操作转换为 O(n^2) 操作。 即使列表不是巨大的,而只是不平凡的大小,这也是一个问题。
相反,我们只需要在不使用foreach
的情况下遍历集合,以便我们引用Node
对象,并且通过正确管理遍历和修改集合的时间/方式,我们不会获得并发修改异常。
var currentNode = list.First;
while (currentNode != null)
{
if (currentNode.Value.color == "blue")
{
var toRemove = currentNode;
currentNode = currentNode.Next;
list.Remove(toRemove);
}
else
{
currentNode = currentNode.Next;
}
}
它不是那么漂亮,但它会更有效率。
现在,理想情况下,LinkedList
会有一个RemoveAll
的方法,这样你就不需要一直为此烦恼。 可悲的是,它没有。 不过,从好的方面来说,您可以添加自己的扩展方法:
public static void RemoveAll<T>(this LinkedList<T> list, Func<T, bool> predicate)
{
var currentNode = list.First;
while (currentNode != null)
{
if (predicate(currentNode.Value))
{
var toRemove = currentNode;
currentNode = currentNode.Next;
list.Remove(toRemove);
}
else
{
currentNode = currentNode.Next;
}
}
}
现在我们可以写:
carList.RemoveAll(car => car.paint.color == "blue");