我的任务是维护一些遗留代码。至少在我看来,我的前任对C#非常有经验。但是,我注意到他留下了一些奇怪的代码。以此为例:
Storage.Clients.RemoveAll(x => x == null || x.IsOutdated());
foreach(Client client in Storage.Clients) {
//do something
}
要清除null和过时客户端的列表,但在我看来,如果这样做会更有效:
for(int i = 0; i < Storage.Clients.Count; i++) {
if(Storage.Clients[i] == null || Storage.Clients[i].IsOutdated())
Storage.Clients.RemoveAt(i);
else
//do something
}
我的理由是,不是迭代两次(一次在RemoveAll中,然后在foreach中),它只迭代一次,可能会使性能翻倍。我在这里错过了什么吗?正如我所说,我的前任经验丰富的事实让我不确定。
答案 0 :(得分:6)
在一英里长的赛道上跑一圈,或在半英里长的跑道上跑两圈,速度更快?你认为在半英里的赛道上做两圈会花费两倍的时间吗?
有两个循环,每个循环完成一个循环所做的所有工作的一半,并不会导致两倍的工作量。你会对实际的迭代逻辑有更多的开销,但在几乎所有的环境中,它都非常非常简单。通常(这似乎是这里的情况)在循环体中完成的实际工作将大大压倒实际循环本身所需的工作量(增加循环变量,检查循环是否完成等)
实际上,在您的示例中,原始代码很多更快。当你拨打RemoveAt
时,它需要在删除的项目之后重新排序最后一个项目,所以如果你打电话给它N次,那就是(平均)一半项目的N次重新排序;你最后一点一点地向下移动同一个项目。当您致电RemoveAll
时,它可以同时删除所有项目,并且会根据需要将所有项目的全部移动一次,从而大幅减少重新排序次数列表中的项目,将O(n ^ 2)操作转换为O(n)操作。
正如Aomine的回答中提到的那样,你的实现中也有一个错误,当你删除一个项目时没有调整循环索引,所以你需要在任何其他之前修复它甚至可以考虑。
两者之间也存在语义差异,这可能是也可能不相关。如果&#34;做某事&#34;那么该程序的行为可能会有所不同。查看列表并受到过时项目是否已从中删除的影响。 (这里可能不相关,但一般来说,在考虑组合循环时,您需要考虑在执行第二类操作之前是否将所有第一类操作应用于每个项目实际上是否重要。)
答案 1 :(得分:2)
您的方法很容易出错,因为您在迭代集合时删除了项目,这意味着您可能无法删除所有必需的元素,因为其中一些元素可能会被跳过而您的前辈会被删除方法可以按预期工作。
答案 2 :(得分:2)
您的新代码无效。问题是如果你的清单中有3个项目(项目[0] = A,项目[1] = B,项目[2] = C)
您的检查项目A(项目&#39; 0&#39;)决定将其删除。
这会将列表的大小减小到2,并且列表现在包含(item [0] = B,item [1] = C)
将计数器增加到1,并进行循环的第二次迭代,在本例中为item [1] = C
错误: 你从未检查过B项
答案 3 :(得分:0)
我想你的问题应该与之比较:
maxSemaphores
它可能会快一点,但由于其他答案所指出的可读性和其他问题,我认为它不值得。