編輯:在這個問題的底部發布的不同技術的基準。
我有一個非常大List<int>的整數。我想從List<int>. 哪種技術最有效?我通常會使用.Remove(3)擴展名直到它回傳false,但我擔心每次呼叫.Remove(3)內部都會List<int>不必要地回圈整個程序。
編輯:在評論中建議嘗試
TheList = TheList.Where(x => x != 3).ToList();
但我需要在不實體化新 List 的情況下洗掉元素。
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--;
}
}
有沒有更好的方法來做到這一點?
基準
我測驗了三種技術來從具有 100,000 個元素的陣列中洗掉 10,138:上面顯示的兩種,以及 Serg 在答案中推薦的一種。這些是結果:
- “while”回圈:179.6808ms
- “for”回圈:65.5099ms
- 'RemoveAll' 謂詞:0.5982ms

基準代碼:
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>
uj5u.com熱心網友回復:
您可以使用List<T>.RemoveAll并傳遞您的謂詞 - https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.removeall?view=net-6.0#System_Collections_Generic_List_1_RemoveAll_System_Predicate__0_ _ 。這保證是線性復雜度O(list.Count)
TheList.RemoveAll(x=>x==3);
此外,RemoveAll在內部執行一些特定于 GC 的事情,所以我認為在某些情況下,這可能會為簡單的手工回圈實作提供一些額外的性能優勢(但我在這里不確定)。
如果你想自己做這一切,你可以查看RemoveAll 這里的實作。一般來說,它只是一個while回圈,就像你的問題一樣。
此外,正如我們從 GitHub 實作中看到的(以及 Jon Skeet 在評論中提到的),洗掉操作導致串列的其余部分(第一個洗掉的專案之后的所有專案)在空閑空間上被復制(移動),通過洗掉引入. 因此,如果您有非常大的串列和/或想要頻繁洗掉某些內容,您可以考慮切換到其他資料結構,例如鏈表。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/362945.html
