首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从列表中移除具有一定价值的元素的最有效方法?C#

从列表中移除具有一定价值的元素的最有效方法?C#
EN

Stack Overflow用户
提问于 2021-11-21 17:23:11
回答 1查看 273关注 0票数 4

编辑:在这个问题的底部发布的不同技术的基准。

我有一个非常大的List<int>,里面满是整数。我想从List<int>中删除每一个"3“的出现。哪种技术能最有效地做到这一点?我通常会使用.Remove(3)扩展,直到它返回false为止,但我担心每个对.Remove(3)的调用都不必要地在整个List<int>中循环。

编辑:建议在评论中尝试

TheList = TheList.Where(x => x != 3).ToList();

但是,我需要删除元素而不实例化一个新的列表

代码语言:javascript
复制
var TheList = new List<int> { 5, 7, 8, 2, 8, 3, 1, 0, 6, 3, 9, 3, 5, 2, 7, 9, 3, 5, 5, 1, 0, 4, 5, 3, 5, 8, 2, 3 };

//technique 1
//this technique has the shortest amount of code,
//but I fear that every time the Remove() method is called,
//the entire list is internally looped over again starting at index 0

while (TheList.Remove(3)) { }

//technique 2
//this technique is an attempt to keep the keep the list from
//being looped over every time an element is removed

for (var i = 0; i < TheList.Count; i++)
{
    if (TheList[i] == 3)
    {
        TheList.RemoveAt(i);
        i--;
    }
}

有什么更好的方法吗?

基准测试

我测试了三种技术,从一个包含10万个元素的数组中删除10,138 :上面所示的两种,以及Serg在回答中推荐的一种。以下是研究结果:

  1. ‘'while’循环: 179.6808ms
  2. 循环: 65.5099ms
  3. “RemoveAll”谓词: 0.5982ms

基准代码:

代码语言:javascript
复制
var RNG = new Random();
//inclusive min and max random number
Func<int, int, int> RandomInt = delegate (int min, int max) { return RNG.Next(min - 1, max) + 1; };

var TheList = new List<int>();
var ThreeCount = 0;
for (var i = 0; i < 100000; i++)
{
    var TheInteger = RandomInt(0, 9);
    if (TheInteger == 3) { ThreeCount++; }
    TheList.Add(TheInteger);
}
var Technique1List = TheList.ToList();
var Technique2List = TheList.ToList();
var Technique3List = TheList.ToList();
<div style="background-color:aquamarine;color:#000000;">Time to remove @ThreeCount items</div>

//technique 1
var Technique1Stopwatch = Stopwatch.StartNew();
while (Technique1List.Remove(3)) { }
var Technique1Time = Technique1Stopwatch.Elapsed.TotalMilliseconds;
<div style="background-color:#ffffff;color:#000000;">Technique 1: @(Technique1Time)ms ('while' loop)</div>

//technique 2
var Technique2Stopwatch = Stopwatch.StartNew();
for (var i = 0; i < Technique2List.Count; i++)
{
    if (Technique2List[i] == 3)
    {
        Technique2List.RemoveAt(i);
        i--;
    }
}
var Technique2Time = Technique2Stopwatch.Elapsed.TotalMilliseconds;
<div style="background-color:#ffffff;color:#000000;">Technique 2: @(Technique2Time)ms ('for' loop)</div>

//technique 3
var Technique3Stopwatch = Stopwatch.StartNew();
var RemovedCount = Technique3List.RemoveAll(x => x == 3);
var Technique3Time = Technique3Stopwatch.Elapsed.TotalMilliseconds;
<div style="background-color:#ffffff;color:#000000;">Technique 3: @(Technique3Time)ms ('RemoveAll' predicate)</div>
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-11-21 17:40:37

您只需使用List<T>.RemoveAll并传递谓词- __。这保证了线性复杂度O(list.Count)

代码语言:javascript
复制
TheList.RemoveAll(x=>x==3);

此外,RemoveAll在内部执行一些特定于GC的功能,因此我认为在某些情况下,这可能会为简单的手工循环实现提供一些额外的性能优势(但我在这里不确定)。

如果您想自己做这件事,可以查看RemoveAll 这里的实现。通常,它只是一个while循环,就像在您的问题中一样。

此外,正如我们从GitHub实现中看到的(正如Jon在注释中提到的那样),删除操作将导致列表的其余部分(在第一个已删除项之后的所有项)被复制(移动)在空闲空间上,这是由删除引入的。因此,如果您有非常大的列表和/或希望频繁删除某些内容,您可以考虑切换到其他数据结构,例如链接列表。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70056930

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档