วิธีลบองค์ประกอบออกจากรายการทั่วไปในขณะที่วนซ้ำมัน?


451

ฉันกำลังมองหารูปแบบที่ดีกว่าสำหรับการทำงานกับรายการองค์ประกอบที่ต้องดำเนินการแต่ละรายการและจากนั้นผลลัพธ์จะถูกลบออกจากรายการ

คุณไม่สามารถใช้.Remove(element)ภายในforeach (var element in X)(เพราะมันจะส่งผลในCollection was modified; enumeration operation may not execute.ข้อยกเว้น) ... คุณยังไม่สามารถใช้for (int i = 0; i < elements.Count(); i++)และเพราะมันรบกวนตำแหน่งปัจจุบันของคุณในการเก็บรวบรวมเทียบกับ.RemoveAt(i)i

มีวิธีที่สง่างามในการทำเช่นนี้หรือไม่?

คำตอบ:


734

วนซ้ำรายการของคุณย้อนกลับด้วย for for loop:

for (int i = safePendingList.Count - 1; i >= 0; i--)
{
    // some code
    // safePendingList.RemoveAt(i);
}

ตัวอย่าง:

var list = new List<int>(Enumerable.Range(1, 10));
for (int i = list.Count - 1; i >= 0; i--)
{
    if (list[i] > 5)
        list.RemoveAt(i);
}
list.ForEach(i => Console.WriteLine(i));

คุณสามารถใช้เมธอด RemoveAllพร้อมเพรดิเคตเพื่อทดสอบกับ:

safePendingList.RemoveAll(item => item.Value == someValue);

นี่เป็นตัวอย่างง่าย ๆ ที่จะสาธิต:

var list = new List<int>(Enumerable.Range(1, 10));
Console.WriteLine("Before:");
list.ForEach(i => Console.WriteLine(i));
list.RemoveAll(i => i > 5);
Console.WriteLine("After:");
list.ForEach(i => Console.WriteLine(i));

19
สำหรับผู้ที่มาจาก Java รายการของ C # จะเหมือนกับ ArrayList ในการแทรก / เอาออกคือ O (n) และการดึงผ่านดัชนีคือ O (1) นี่ไม่ใช่รายการเชื่อมโยงแบบดั้งเดิม ดูเหมือนว่าโชคร้ายเล็กน้อย C # ใช้คำว่า "List" เพื่ออธิบายโครงสร้างข้อมูลนี้เนื่องจากคำนึงถึงรายการเชื่อมโยงแบบคลาสสิค
Jarrod Smith

79
ไม่มีสิ่งใดในชื่อ 'รายการ' ที่ระบุว่า 'LinkedList' คนที่มาจากภาษาอื่นที่ไม่ใช่ Java อาจสับสนเมื่อมันเป็นรายการเชื่อมโยง
GolezTrol

5
ฉันสิ้นสุดที่นี่ผ่านการค้นหา vb.net ดังนั้นในกรณีที่ทุกคนต้องการไวยากรณ์ที่เทียบเท่า vb.net สำหรับ RemoveAll:list.RemoveAll(Function(item) item.Value = somevalue)
pseudocoder

1
นอกจากนี้ยังทำงานในชวามีการดัดแปลงน้อยที่สุด: .size()แทน.Countและแทน.remove(i) .removeAt(i)เคลฟเวอร์ - ขอบคุณ!
Autumn Leonard

2
ฉันทำการทดสอบเล็กน้อยเพื่อประสิทธิภาพและมันกลับกลายเป็นว่าRemoveAll()ต้องใช้เวลามากถึงสามเท่าของการforวนถอยหลัง ดังนั้นฉันจึงติดอยู่กับลูปอย่างน้อยที่สุดก็ในส่วนที่สำคัญ
Crusha K. Rool

84

ทางออกที่ง่ายและตรงไปตรงมา:

ใช้มาตรฐานสำหรับวงวนย้อนหลังในคอลเลกชันของคุณและRemoveAt(i)เพื่อลบองค์ประกอบ


2
โปรดทราบว่าการลบรายการทีละรายการนั้นไม่มีประสิทธิภาพหากรายการของคุณมีหลายรายการ มันมีศักยภาพที่จะเป็น O (n ^ 2) ลองนึกภาพรายการที่มีสองพันรายการที่เกิดขึ้นที่รายการแรกพันรายการทั้งหมดถูกลบ การลบแต่ละรายการจะบังคับให้คัดลอกรายการในภายหลังทั้งหมดดังนั้นคุณจึงต้องคัดลอกรายการนับพันรายการต่อพันล้านครั้ง สิ่งนี้ไม่ได้เกิดจากการทำซ้ำแบบย้อนกลับ แต่เป็นเพราะการลบแบบครั้งต่อครั้ง RemoveAll ทำให้แน่ใจว่าแต่ละรายการจะถูกคัดลอกมากที่สุดหนึ่งครั้งจึงเป็นแบบเชิงเส้น การกำจัดครั้งเดียวอาจช้ากว่าพันล้านครั้ง O (n) กับ O (n ^ 2)
Bruce Dawson

71

การวนซ้ำแบบย้อนกลับควรเป็นสิ่งแรกที่คุณควรคำนึงถึงเมื่อคุณต้องการลบองค์ประกอบออกจากคอลเลกชันในขณะที่วนซ้ำ

โชคดีที่มีวิธีแก้ปัญหาที่สง่างามกว่าการเขียนลูป for ซึ่งเกี่ยวข้องกับการพิมพ์ที่ไม่จำเป็นและอาจเกิดข้อผิดพลาดได้ง่าย

ICollection<int> test = new List<int>(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

foreach (int myInt in test.Reverse<int>())
{
    if (myInt % 2 == 0)
    {
        test.Remove(myInt);
    }
}

7
มันทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน เรียบง่ายและสวยงามและจำเป็นต้องมีการเปลี่ยนแปลงเล็กน้อยกับรหัสของฉัน
Stephen MacDougall

1
นี่เป็นเพียงฟลิปปิ้นอัจฉริยะหรือไม่ และฉันเห็นด้วยกับ @StephenMacDougall ฉันไม่จำเป็นต้องใช้ C ++ 'เหล่านี้ในการวนซ้ำและเพิ่งเข้าร่วมกับ LINQ ตามที่ตั้งใจไว้
Piotr Kula

5
ฉันไม่เห็นข้อได้เปรียบเหนือ foreach แบบง่าย (int myInt ใน test.ToList ()) {ถ้า (myInt% 2 == 0) {test.Remove (myInt); }} คุณยังคงต้องจัดสรรสำเนาสำหรับ Reverse และแนะนำ Huh? ขณะนี้ - ทำไมจึงมีการย้อนกลับ
jahav

11
@jedesah ใช่Reverse<T>()สร้างตัววนซ้ำที่ทำรายการย้อนหลัง แต่จะจัดสรรบัฟเฟอร์พิเศษที่มีขนาดเดียวกับรายการตัวมันเอง ( อ้างอิงsource.microsoft.com/#System.Core/System/Linq/… ) Reverse<T>ไม่เพียงแค่ผ่านรายการต้นฉบับในลำดับย้อนกลับ (โดยไม่มีการจัดสรรหน่วยความจำเพิ่มเติม) ดังนั้นทั้งสองToList()และReverse()มีการใช้หน่วยความจำเท่ากัน (ทั้งสร้างสำเนา) แต่ToList()ไม่ได้ทำอะไรกับข้อมูล ด้วยReverse<int>()ฉันจะสงสัยว่าทำไมรายการกลับรายการด้วยเหตุผลอะไร
jahav

1
@ jahav ฉันเห็นประเด็นของคุณ มันค่อนข้างน่าผิดหวังที่การใช้งานของReverse<T>()สร้างบัฟเฟอร์ใหม่ฉันไม่แน่ใจว่าฉันเข้าใจว่าทำไมจึงมีความจำเป็น สำหรับฉันแล้วดูเหมือนว่าEnumerableอย่างน้อยในบางกรณีอาจเป็นไปได้ที่จะทำซ้ำแบบย้อนกลับโดยไม่ต้องจัดสรรหน่วยความจำเชิงเส้น
jedesah

68
 foreach (var item in list.ToList()) {
     list.Remove(item);
 }

หากคุณเพิ่ม ".ToList ()"ในรายการของคุณ (หรือผลลัพธ์ของการสืบค้น LINQ) คุณสามารถลบ "รายการ" โดยตรงจาก "รายการ" โดยไม่มี " คอลเลกชันที่น่ากลัว" ถูกแก้ไขการดำเนินการแจงนับอาจไม่ทำงาน " ความผิดพลาด คอมไพเลอร์สร้างสำเนาของ "รายการ" เพื่อให้คุณสามารถลบออกได้อย่างปลอดภัย

ในขณะที่รูปแบบนี้ไม่ได้มีประสิทธิภาพสุดก็มีความรู้สึกเป็นธรรมชาติและมีความยืดหยุ่นพอสำหรับเกือบทุกสถานการณ์ เช่นเมื่อคุณต้องการบันทึก "รายการ" แต่ละรายการลงในฐานข้อมูลและลบออกจากรายการเฉพาะเมื่อฐานข้อมูลบันทึกสำเร็จ


6
นี่เป็นทางออกที่ดีที่สุดหากประสิทธิภาพไม่สำคัญ
IvanP

2
นี่คือเร็วขึ้นและสามารถอ่านได้มากขึ้น: list.RemoveAll (i => true);
santiago arizti

1
@Greg Little ฉันเข้าใจคุณถูกต้อง - เมื่อคุณเพิ่ม ToList () คอมไพเลอร์ต้องผ่านคอลเล็กชันที่คัดลอก แต่ลบออกจากต้นฉบับหรือไม่
Pyrejkee

หากรายการของคุณมีรายการที่ซ้ำกันและคุณต้องการที่จะลบรายการที่เกิดขึ้นในภายหลังรายการนี้จะไม่ลบรายการที่ผิดหรือไม่
ทิม MB

สิ่งนี้ใช้ได้กับรายการวัตถุหรือไม่
Tom el Safadi

23

เลือกองค์ประกอบที่คุณไม่ต้องการมากกว่าการพยายามที่จะเอาองค์ประกอบที่คุณไม่ต้องการ สิ่งนี้ง่ายกว่ามาก (และโดยทั่วไปก็มีประสิทธิภาพมากกว่าด้วย) กว่าการลบองค์ประกอบ

var newSequence = (from el in list
                   where el.Something || el.AnotherThing < 0
                   select el);

ฉันต้องการโพสต์สิ่งนี้เป็นความคิดเห็นเพื่อตอบสนองต่อความคิดเห็นที่เหลือโดย Michael Dillon ด้านล่าง แต่มันยาวเกินไปและอาจมีประโยชน์ในการตอบคำถามของฉัน:

โดยส่วนตัวแล้วฉันจะไม่ลบรายการทีละรายการถ้าคุณต้องการลบออกแล้วเรียกRemoveAllว่าใช้เพรดิเคตและจัดเรียงอาร์เรย์ภายในใหม่เพียงครั้งเดียวในขณะที่RemoveการArray.Copyดำเนินการสำหรับทุกองค์ประกอบที่คุณลบออกRemoveAllมีประสิทธิภาพมากขึ้นอย่างมากมาย

และเมื่อคุณย้อนกลับไปซ้ำในรายการคุณมีดัชนีขององค์ประกอบที่คุณต้องการลบอยู่แล้วดังนั้นมันจะมีประสิทธิภาพมากกว่าในการโทรRemoveAtเพราะRemoveอันดับแรกคือการสำรวจเส้นทางของรายการเพื่อค้นหาดัชนีขององค์ประกอบที่คุณ กำลังพยายามลบ แต่คุณรู้อยู่แล้วว่าดัชนีนั้น

ดังนั้นโดยรวมแล้วฉันไม่เห็นเหตุผลใด ๆ ที่จะโทรRemoveหาความจริง และถ้าเป็นไปได้ให้ใช้โค้ดด้านบนเพื่อสตรีมองค์ประกอบจากรายการตามต้องการดังนั้นจึงไม่มีการสร้างโครงสร้างข้อมูลที่สองเลย


1
หรือเพิ่มตัวชี้ไปยังองค์ประกอบที่ไม่ต้องการลงในรายการที่สองจากนั้นหลังจากที่ลูปของคุณสิ้นสุดให้ทำซ้ำรายการลบและใช้เพื่อลบองค์ประกอบ
Michael Dillon

22

การใช้ ToArray () ในรายการทั่วไปช่วยให้คุณสามารถลบ (รายการ) ในรายการทั่วไปของคุณ:

        List<String> strings = new List<string>() { "a", "b", "c", "d" };
        foreach (string s in strings.ToArray())
        {
            if (s == "b")
                strings.Remove(s);
        }

2
สิ่งนี้ไม่ผิด แต่ฉันต้องชี้ให้เห็นว่านี่เป็นการข้ามความจำเป็นในการสร้างรายการ "หน่วยเก็บข้อมูล" ที่ 2 ของรายการที่คุณต้องการลบออกโดยมีค่าใช้จ่ายในการคัดลอกรายการทั้งหมดไปยังอาร์เรย์ รายการองค์ประกอบที่หยิบด้วยมือลำดับที่ 2 อาจมีรายการน้อยกว่า
Ahmad Mageed

20

การใช้. ToList () จะทำสำเนารายการของคุณดังที่อธิบายไว้ในคำถามนี้ ToList () - สร้างรายการใหม่หรือไม่

โดยใช้ ToList () คุณสามารถลบออกจากรายการเดิมของคุณเพราะคุณทำซ้ำจริง ๆ แล้วคัดลอก

foreach (var item in listTracked.ToList()) {    

        if (DetermineIfRequiresRemoval(item)) {
            listTracked.Remove(item)
        }

     }

1
แต่จากมุมมองประสิทธิภาพคุณกำลังคัดลอกรายการซึ่งอาจใช้เวลาสักครู่ วิธีที่ดีและง่ายต่อการทำ แต่ด้วยประสิทธิภาพที่ไม่ดีนัก
Florian K

12

หากฟังก์ชั่นที่กำหนดว่ารายการที่จะลบไม่มีผลข้างเคียงและไม่กลายพันธุ์รายการ (เป็นฟังก์ชั่นแท้) โซลูชันที่เรียบง่ายและมีประสิทธิภาพ (เวลาเชิงเส้น) คือ:

list.RemoveAll(condition);

หากมีผลข้างเคียงฉันจะใช้สิ่งที่ชอบ:

var toRemove = new HashSet<T>();
foreach(var item in items)
{
     ...
     if(condition)
          toRemove.Add(item);
}
items.RemoveAll(toRemove.Contains);

นี่เป็นเวลาเชิงเส้นโดยสมมติว่าแฮชดี แต่มีหน่วยความจำเพิ่มขึ้นเนื่องจาก hashset ใช้

สุดท้ายหากรายการของคุณเป็นเพียงการIList<T>แทนList<T>ผมขอแนะนำให้คำตอบของฉันฉันจะทำเช่นนี้ iterator foreach พิเศษหรือไม่? . สิ่งนี้จะมีไทม์มิ่งเชิงเส้นเนื่องจากการใช้งานทั่วไปของIList<T>เมื่อเทียบกับรันไทม์กำลังสองของคำตอบอื่น ๆ


11

เนื่องจากการลบใด ๆ เกิดขึ้นในสภาพที่คุณสามารถใช้ได้

list.RemoveAll(item => item.Value == someValue);

นั่นเป็นทางออกที่ดีที่สุดหากการประมวลผลไม่กลายพันธุ์รายการและไม่มีผลข้างเคียง
CodesInChaos


8

คุณไม่สามารถใช้ foreach ได้ แต่คุณสามารถทำซ้ำไปข้างหน้าและจัดการตัวแปรดัชนีลูปของคุณเมื่อคุณลบรายการออกเช่น:

for (int i = 0; i < elements.Count; i++)
{
    if (<condition>)
    {
        // Decrement the loop counter to iterate this index again, since later elements will get moved down during the remove operation.
        elements.RemoveAt(i--);
    }
}

โปรดทราบว่าโดยทั่วไปเทคนิคเหล่านี้ทั้งหมดขึ้นอยู่กับพฤติกรรมของคอลเลกชันที่ถูกทำซ้ำ เทคนิคที่แสดงที่นี่จะทำงานกับรายการมาตรฐาน (T) (มันค่อนข้างเป็นไปได้ที่จะเขียนระดับคอลเลกชันของคุณเองและ iterator ที่ไม่ให้การกำจัดรายการระหว่างห่วง foreach.)


6

การใช้RemoveหรือRemoveAtในรายการขณะ iterating กว่ารายการที่ได้รับการจงใจทำยากเพราะมันมักจะเป็นสิ่งที่ไม่ถูกต้องที่จะทำ คุณอาจสามารถทำให้มันทำงานได้ด้วยเล่ห์เหลี่ยมบางอย่าง แต่มันจะช้ามาก ทุกครั้งที่คุณโทรRemoveจะต้องสแกนรายการทั้งหมดเพื่อค้นหาองค์ประกอบที่คุณต้องการลบ ทุกครั้งที่คุณเรียกRemoveAtมันจะต้องย้ายองค์ประกอบที่ตามมา 1 ตำแหน่งไปทางซ้าย เช่นการแก้ปัญหาใด ๆ ที่ใช้RemoveหรือRemoveAtจะต้องใช้เวลากำลังสองO (n²)

ใช้RemoveAllถ้าคุณสามารถ มิฉะนั้นต่อไปนี้รูปแบบจะกรองรายการในสถานที่ในเส้นเวลาO (n)

// Create a list to be filtered
IList<int> elements = new List<int>(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
// Filter the list
int kept = 0;
for (int i = 0; i < elements.Count; i++) {
    // Test whether this is an element that we want to keep.
    if (elements[i] % 3 > 0) {
        // Add it to the list of kept elements.
        elements[kept] = elements[i];
        kept++;
    }
}
// Unfortunately IList has no Resize method. So instead we
// remove the last element of the list until: elements.Count == kept.
while (kept < elements.Count) elements.RemoveAt(elements.Count-1);

3

ฉันหวังว่า "รูปแบบ" เป็นอะไรเช่นนี้:

foreach( thing in thingpile )
{
    if( /* condition#1 */ )
    {
        foreach.markfordeleting( thing );
    }
    elseif( /* condition#2 */ )
    {
        foreach.markforkeeping( thing );
    }
} 
foreachcompleted
{
    // then the programmer's choices would be:

    // delete everything that was marked for deleting
    foreach.deletenow(thingpile); 

    // ...or... keep only things that were marked for keeping
    foreach.keepnow(thingpile);

    // ...or even... make a new list of the unmarked items
    others = foreach.unmarked(thingpile);   
}

สิ่งนี้จะจัดแนวโค้ดกับกระบวนการที่เกิดขึ้นในสมองของโปรแกรมเมอร์


1
ง่ายพอสมควร เพียงแค่สร้างอาร์เรย์แฟล็กบูลีน (ใช้ประเภท 3 สถานะเช่นNullable<bool>หากคุณต้องการอนุญาตให้ไม่มีเครื่องหมาย) จากนั้นใช้หลัง foreach เพื่อลบ / เก็บรายการ
Dan Bechard

3

โดยสมมติว่ากริยาเป็นที่พักแบบบูลขององค์ประกอบว่าถ้ามันเป็นความจริงแล้วองค์ประกอบที่ควรจะออก:

        int i = 0;
        while (i < list.Count())
        {
            if (list[i].predicate == true)
            {
                list.RemoveAt(i);
                continue;
            }
            i++;
        }

ฉันให้ upvote นี้เพราะบางครั้งมันอาจมีประสิทธิภาพมากกว่าในการเลื่อนดูรายการตามลำดับ (ไม่ใช่ลำดับกลับกัน) บางทีคุณอาจหยุดเมื่อค้นหารายการแรกที่จะไม่ลบเนื่องจากมีการสั่งซื้อรายการ (ลองจินตนาการถึง 'ตัวแบ่ง' โดยที่ i ++ อยู่ในตัวอย่างนี้
FrankKrumnow

3

ฉันจะมอบหมายรายการจากแบบสอบถาม LINQ ที่กรององค์ประกอบที่คุณไม่ต้องการเก็บไว้

list = list.Where(item => ...).ToList();

หากรายการมีขนาดใหญ่มากไม่ควรมีปัญหาประสิทธิภาพที่สำคัญในการทำเช่นนี้


3

วิธีที่ดีที่สุดในการลบรายการออกจากรายการในขณะที่วนซ้ำมันคือการใช้ RemoveAll()วิธีที่ดีที่สุดที่จะลบรายการจากรายการในขณะที่การทำซ้ำมากกว่านั้นคือการใช้งานแต่ข้อกังวลหลักที่เขียนโดยคนคือพวกเขาต้องทำสิ่งที่ซับซ้อนบางอย่างภายในวงและ / หรือมีกรณีเปรียบเทียบที่ซับซ้อน

การแก้ปัญหาคือยังคงใช้RemoveAll()แต่ใช้สัญกรณ์นี้:

var list = new List<int>(Enumerable.Range(1, 10));
list.RemoveAll(item => 
{
    // Do some complex operations here
    // Or even some operations on the items
    SomeFunction(item);
    // In the end return true if the item is to be removed. False otherwise
    return item > 5;
});

2
foreach(var item in list.ToList())

{

if(item.Delete) list.Remove(item);

}

เพียงสร้างรายการใหม่ทั้งหมดจากรายการแรก ฉันพูดว่า "ง่าย" แทนที่จะเป็น "ถูกต้อง" เนื่องจากการสร้างรายการใหม่ทั้งหมดอาจมาพร้อมกับประสิทธิภาพที่เหนือชั้นกว่าวิธีก่อนหน้านี้ (ฉันไม่ได้ใส่ใจกับการเปรียบเทียบใด ๆ ) โดยทั่วไปฉันชอบรูปแบบนี้ก็เป็นประโยชน์ในการเอาชนะ ข้อ จำกัด Linq-To-Entities

for(i = list.Count()-1;i>=0;i--)

{

item=list[i];

if (item.Delete) list.Remove(item);

}

วิธีนี้วนรอบรายการย้อนหลังด้วย For old แบบธรรมดา การส่งต่อนี้อาจเป็นปัญหาได้หากขนาดของคอลเลกชันเปลี่ยนแปลง แต่กลับควรมีความปลอดภัยเสมอ


1

คัดลอกรายการที่คุณกำลังทำซ้ำ จากนั้นลบออกจากการคัดลอกและ interate ต้นฉบับ การย้อนกลับทำให้เกิดความสับสนและทำงานได้ไม่ดีเมื่อวนซ้ำกัน

var ids = new List<int> { 1, 2, 3, 4 };
var iterableIds = ids.ToList();

Parallel.ForEach(iterableIds, id =>
{
    ids.Remove(id);
});

1

ฉันจะทำเช่นนี้

using System.IO;
using System;
using System.Collections.Generic;

class Author
    {
        public string Firstname;
        public string Lastname;
        public int no;
    }

class Program
{
    private static bool isEven(int i) 
    { 
        return ((i % 2) == 0); 
    } 

    static void Main()
    {    
        var authorsList = new List<Author>()
        {
            new Author{ Firstname = "Bob", Lastname = "Smith", no = 2 },
            new Author{ Firstname = "Fred", Lastname = "Jones", no = 3 },
            new Author{ Firstname = "Brian", Lastname = "Brains", no = 4 },
            new Author{ Firstname = "Billy", Lastname = "TheKid", no = 1 }
        };

        authorsList.RemoveAll(item => isEven(item.no));

        foreach(var auth in authorsList)
        {
            Console.WriteLine(auth.Firstname + " " + auth.Lastname);
        }
    }
}

เอาท์พุท

Fred Jones
Billy TheKid

0

ผมพบว่าตัวเองอยู่ในสถานการณ์ที่คล้ายกันที่ฉันมีการลบทุก n THList<T>องค์ประกอบที่กำหนด

for (int i = 0, j = 0, n = 3; i < list.Count; i++)
{
    if ((j + 1) % n == 0) //Check current iteration is at the nth interval
    {
        list.RemoveAt(i);
        j++; //This extra addition is necessary. Without it j will wrap
             //down to zero, which will throw off our index.
    }
    j++; //This will always advance the j counter
}

0

ค่าใช้จ่ายในการลบรายการออกจากรายการเป็นสัดส่วนกับจำนวนรายการที่จะถูกลบออก ในกรณีที่ครึ่งแรกของรายการมีคุณสมบัติสำหรับการลบวิธีการใด ๆ ที่ยึดตามการลบแต่ละรายการจะต้องดำเนินการเกี่ยวกับการคัดลอกรายการ N * N / 4 ซึ่งอาจมีราคาแพงมากหากรายการมีขนาดใหญ่ .

วิธีที่เร็วกว่าคือการสแกนผ่านรายการเพื่อค้นหารายการแรกที่จะลบ (ถ้ามี) จากนั้นไปที่จุดนั้นไปข้างหน้าคัดลอกแต่ละรายการที่ควรเก็บไว้ในจุดที่เป็นของมัน เมื่อดำเนินการเสร็จแล้วหากควรเก็บรักษารายการ R รายการ R แรกในรายการจะเป็นรายการ R เหล่านั้นและรายการทั้งหมดที่ต้องการลบจะอยู่ที่ท้าย หากมีการลบรายการเหล่านั้นในลำดับย้อนหลังระบบจะไม่ต้องคัดลอกรายการใด ๆ ดังนั้นหากรายการมีรายการ N รายการซึ่งรายการ R ซึ่งรวมถึง F แรกทั้งหมดถูกเก็บรักษาไว้จะต้อง คัดลอกรายการ RF และลดขนาดรายการด้วย NR รายการหนึ่งรายการ เวลาเชิงเส้นทั้งหมด


0

แนวทางของฉันคือฉันสร้างรายการดัชนีครั้งแรกซึ่งควรลบออก หลังจากนั้นฉันวนรอบดัชนีและลบรายการออกจากรายการเริ่มต้น ดูเหมือนว่านี้:

var messageList = ...;
// Restrict your list to certain criteria
var customMessageList = messageList.FindAll(m => m.UserId == someId);

if (customMessageList != null && customMessageList.Count > 0)
{
    // Create list with positions in origin list
    List<int> positionList = new List<int>();
    foreach (var message in customMessageList)
    {
        var position = messageList.FindIndex(m => m.MessageId == message.MessageId);
        if (position != -1)
            positionList.Add(position);
    }
    // To be able to remove the items in the origin list, we do it backwards
    // so that the order of indices stays the same
    positionList = positionList.OrderByDescending(p => p).ToList();
    foreach (var position in positionList)
    {
        messageList.RemoveAt(position);
    }
}

0

ในวิธีง่ายๆคือการทำเครื่องหมายรายการที่คุณต้องการลบจากนั้นสร้างรายการใหม่เพื่อทำซ้ำ ...

foreach(var item in list.ToList()){if(item.Delete) list.Remove(item);}  

หรือแม้กระทั่งใช้ linq ที่ง่ายยิ่งขึ้น ....

list.RemoveAll(p=>p.Delete);

แต่มันก็คุ้มค่าที่จะพิจารณาว่างานหรือกระทู้อื่น ๆ จะสามารถเข้าถึงรายการเดียวกันในเวลาเดียวกันกับที่คุณกำลังยุ่งอยู่กับการลบหรือไม่และอาจใช้ ConcurrentList แทน


0

ติดตามองค์ประกอบที่จะลบด้วยคุณสมบัติและลบออกหลังจากกระบวนการ

using System.Linq;

List<MyProperty> _Group = new List<MyProperty>();
// ... add elements

bool cond = true;
foreach (MyProperty currObj in _Group)
{
    if (cond) 
    {
        // SET - element can be deleted
        currObj.REMOVE_ME = true;
    }
}
// RESET
_Group.RemoveAll(r => r.REMOVE_ME);

0

เพียงแค่ต้องการเพิ่ม 2 เซ็นต์ของฉันในกรณีนี้จะช่วยให้ทุกคนฉันมีปัญหาที่คล้ายกัน แต่จำเป็นต้องลบองค์ประกอบหลายรายการจากรายการอาร์เรย์ในขณะที่มันถูกทำซ้ำมากกว่า คำตอบ upvoted ที่สูงที่สุดทำเพื่อฉันจนกระทั่งส่วนใหญ่เกิดข้อผิดพลาดและรู้ว่าดัชนีนั้นมีขนาดใหญ่กว่าขนาดของรายการอาร์เรย์ในบางกรณีเนื่องจากมีการลบองค์ประกอบหลายรายการ แต่ดัชนีของวงไม่ได้เก็บไว้ ติดตามของที่ ฉันแก้ไขสิ่งนี้ด้วยการตรวจสอบง่าย ๆ :

ArrayList place_holder = new ArrayList();
place_holder.Add("1");
place_holder.Add("2");
place_holder.Add("3");
place_holder.Add("4");

for(int i = place_holder.Count-1; i>= 0; i--){
    if(i>= place_holder.Count){
        i = place_holder.Count-1; 
    }

// some method that removes multiple elements here
}

-4
myList.RemoveAt(i--);

simples;

1
ที่simples;นี่ทำอะไร
Denny

6
ผู้แทนของ .. มันลดคำตอบของฉันทุกครั้งที่มันวิ่ง
SW

SW ROFL! ต้องรักความคิดเห็นของคุณ
Erick Brown
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.