Saya mencari pola yang lebih baik untuk bekerja dengan daftar elemen yang masing-masing perlu diproses dan kemudian tergantung pada hasil yang dihapus dari daftar.
Anda tidak dapat menggunakan .Remove(element)
di dalam foreach (var element in X)
(karena itu menghasilkan Collection was modified; enumeration operation may not execute.
pengecualian) ... Anda juga tidak dapat menggunakan for (int i = 0; i < elements.Count(); i++)
dan .RemoveAt(i)
karena itu mengganggu posisi Anda saat ini dalam koleksi relatif terhadap i
.
Apakah ada cara yang elegan untuk melakukan ini?
list.RemoveAll(Function(item) item.Value = somevalue)
.size()
bukannya.Count
dan.remove(i)
bukannya.removeAt(i)
. Pintar - terima kasih!RemoveAll()
membutuhkan waktu tiga kali lebih banyak daripadafor
loop mundur . Jadi saya pasti tetap dengan loop, setidaknya di bagian yang penting.Solusi sederhana dan mudah:
Gunakan standar untuk loop yang berjalan mundur pada koleksi Anda dan
RemoveAt(i)
untuk menghapus elemen.sumber
Balikkan iterasi harus menjadi hal pertama yang terlintas dalam pikiran ketika Anda ingin menghapus elemen dari Koleksi saat iterasi.
Untungnya, ada solusi yang lebih elegan daripada menulis for for loop yang melibatkan pengetikan yang tidak perlu dan bisa rentan kesalahan.
sumber
Reverse<T>()
membuat iterator yang menelusuri daftar mundur, tetapi mengalokasikan buffer tambahan dengan ukuran yang sama dengan daftar itu sendiri untuk itu ( Referenceource.microsoft.com/#System.Core/System/Linq/… ).Reverse<T>
tidak hanya melalui daftar asli dalam urutan terbalik (tanpa mengalokasikan memori tambahan). Karena itu keduanyaToList()
danReverse()
memiliki konsumsi memori yang sama (keduanya membuat salinan), tetapiToList()
tidak melakukan apa pun untuk data. DenganReverse<int>()
, saya akan bertanya-tanya mengapa daftar dibalik, untuk alasan apa.Reverse<T>()
menciptakan buffer baru, saya tidak yakin saya mengerti mengapa itu perlu. Sepertinya saya bahwa tergantung pada struktur yang mendasariEnumerable
, setidaknya dalam beberapa kasus mungkin untuk mencapai iterasi terbalik tanpa mengalokasikan memori linier.Jika Anda menambahkan ".ToList ()" ke daftar Anda (atau hasil kueri LINQ), Anda dapat menghapus "item" secara langsung dari "daftar" tanpa " Koleksi yang ditakuti telah diubah; operasi penghitungan tidak dapat dijalankan ." kesalahan. Kompiler membuat salinan "daftar", sehingga Anda dapat dengan aman melakukan penghapusan pada array.
Meskipun pola ini tidak sangat efisien, ia memiliki nuansa alami dan cukup fleksibel untuk hampir semua situasi . Seperti ketika Anda ingin menyimpan setiap "item" ke DB dan menghapusnya dari daftar hanya ketika penyimpanan DB berhasil.
sumber
Pilih elemen yang Anda lakukan inginkan daripada mencoba untuk menghapus elemen yang Anda tidak inginkan. Ini jauh lebih mudah (dan umumnya juga lebih efisien) daripada menghapus elemen.
Saya ingin memposting ini sebagai komentar untuk menanggapi komentar yang ditinggalkan oleh Michael Dillon di bawah ini, tetapi terlalu panjang dan mungkin berguna untuk menjawab:
Secara pribadi, saya tidak akan pernah menghapus item satu-per-satu, jika Anda memang perlu dihapus, panggil
RemoveAll
yang mengambil predikat dan hanya mengatur ulang array internal satu kali, sedangkanRemove
melakukanArray.Copy
operasi untuk setiap elemen yang Anda hapus.RemoveAll
jauh lebih efisien.Dan ketika Anda mundur mengulangi daftar, Anda sudah memiliki indeks elemen yang ingin Anda hapus, jadi akan jauh lebih efisien untuk memanggil
RemoveAt
, karenaRemove
pertama-tama lakukan traversal daftar untuk menemukan indeks elemen yang Anda inginkan. Sedang mencoba untuk menghapus, tetapi Anda sudah tahu indeks itu.Jadi semuanya, saya tidak melihat alasan untuk memanggil
Remove
for-loop. Dan idealnya, jika memungkinkan, gunakan kode di atas untuk mengalirkan elemen dari daftar sesuai kebutuhan sehingga tidak ada struktur data kedua yang harus dibuat sama sekali.sumber
Menggunakan ToArray () pada daftar generik memungkinkan Anda untuk melakukan Hapus (item) pada Daftar generik Anda:
sumber
Menggunakan .ToList () akan membuat salinan daftar Anda, seperti yang dijelaskan dalam pertanyaan ini: ToList () - Apakah itu Membuat Daftar Baru?
Dengan menggunakan ToList (), Anda dapat menghapus dari daftar asli Anda, karena Anda benar-benar mengulangi salinan.
sumber
Jika fungsi yang menentukan item mana yang akan dihapus tidak memiliki efek samping dan tidak bermutasi item (itu adalah fungsi murni), solusi sederhana (efisien waktu linear) adalah:
Jika ada efek samping, saya akan menggunakan sesuatu seperti:
Ini masih waktu linier, dengan asumsi hash baik. Tetapi memiliki peningkatan penggunaan memori karena hashset.
Akhirnya jika daftar Anda hanya merupakan
IList<T>
bukanList<T>
saya sarankan jawaban saya untuk Bagaimana saya bisa melakukan iterator foreach khusus ini? . Ini akan memiliki runtime linear yang diberikan implementasi tipikalIList<T>
, dibandingkan dengan runtime kuadrat dari banyak jawaban lainnya.sumber
Karena semua pemindahan dilakukan dengan syarat Anda dapat menggunakan
sumber
sumber
Anda tidak dapat menggunakan foreach, tetapi Anda bisa mengulanginya ke depan dan mengelola variabel indeks loop Anda saat Anda menghapus item, seperti:
Perhatikan bahwa secara umum semua teknik ini bergantung pada perilaku koleksi yang diulang. Teknik yang ditunjukkan di sini akan bekerja dengan Daftar standar (T). (Hal ini sangat mungkin untuk menulis kelas koleksi Anda sendiri dan iterator yang tidak memungkinkan penghapusan item yang selama loop foreach.)
sumber
Menggunakan
Remove
atauRemoveAt
pada daftar sambil mengulangi daftar itu sengaja dibuat sulit, karena hampir selalu merupakan hal yang salah untuk dilakukan . Anda mungkin bisa membuatnya bekerja dengan beberapa trik pintar, tetapi itu akan sangat lambat. Setiap kali Anda menyebutnyaRemove
harus memindai seluruh daftar untuk menemukan elemen yang ingin Anda hapus. Setiap kali Anda menyebutnyaRemoveAt
harus memindahkan elemen 1 posisi berikutnya ke kiri. Dengan demikian, solusi apa pun yang menggunakanRemove
atauRemoveAt
, akan membutuhkan waktu kuadratik, O (n²) .Gunakan
RemoveAll
jika Anda bisa. Jika tidak, pola berikut akan memfilter daftar di tempat dalam waktu linier, O (n) .sumber
Saya berharap "pola" itu seperti ini:
Ini akan menyelaraskan kode dengan proses yang berlangsung di otak programmer.
sumber
Nullable<bool>
, Jika Anda ingin mengizinkan yang tidak ditandai), kemudian gunakan setelah pendahuluan untuk menghapus / menyimpan item.Dengan mengasumsikan bahwa predikat adalah properti Boolean dari suatu elemen, bahwa jika itu benar, maka elemen tersebut harus dihapus:
sumber
Saya akan menetapkan ulang daftar dari kueri LINQ yang memfilter elemen yang tidak ingin Anda pertahankan.
Kecuali jika daftarnya sangat besar, seharusnya tidak ada masalah kinerja yang signifikan dalam melakukan ini.
sumber
Cara terbaik untuk menghapus item dari daftar sambil mengulanginya adalah dengan menggunakannya
RemoveAll()
. Tetapi perhatian utama yang ditulis oleh orang-orang adalah bahwa mereka harus melakukan beberapa hal kompleks di dalam loop dan / atau memiliki kasus perbandingan yang kompleks.Solusinya adalah tetap menggunakan
RemoveAll()
tetapi gunakan notasi ini:sumber
Cukup buat daftar yang sama sekali baru dari yang pertama. Saya mengatakan "Mudah" daripada "Benar" karena membuat daftar yang sama sekali baru mungkin datang dengan kinerja yang lebih tinggi dari metode sebelumnya (saya belum peduli dengan tolok ukur apa pun.) Saya biasanya lebih suka pola ini, ini juga dapat berguna dalam mengatasi Batasan Linq-To-Entities.
Dengan cara ini, siklus melalui daftar mundur dengan loop tua For polos. Melakukan ini ke depan bisa bermasalah jika ukuran koleksi berubah, tetapi mundur harus selalu aman.
sumber
Salin daftar yang sedang Anda iterasi. Kemudian hapus dari salinan dan interate yang asli. Mundur adalah membingungkan dan tidak berfungsi dengan baik saat perulangan paralel.
sumber
Saya akan melakukan ini
KELUARAN
sumber
Saya menemukan diri saya dalam situasi yang sama di mana aku harus menghapus setiap n th elemen dalam diberikan
List<T>
.sumber
Biaya menghapus item dari daftar sebanding dengan jumlah item setelah item dihapus. Dalam kasus di mana paruh pertama item memenuhi syarat untuk dihapus, pendekatan apa pun yang didasarkan pada penghapusan item secara terpisah akan berakhir dengan melakukan sekitar N * N / 4 operasi penyalinan item, yang bisa menjadi sangat mahal jika daftar besar .
Pendekatan yang lebih cepat adalah memindai melalui daftar untuk menemukan item pertama yang akan dihapus (jika ada), dan kemudian mulai dari itu salin setiap item yang harus disimpan ke tempat di mana barang itu berada. Setelah ini selesai, jika item R harus dipertahankan, item R pertama dalam daftar akan menjadi item R tersebut, dan semua item yang membutuhkan penghapusan akan berada di akhir. Jika item-item itu dihapus dalam urutan terbalik, sistem tidak akan akhirnya harus menyalin salah satu dari mereka, jadi jika daftar memiliki N item yang item R, termasuk semua F pertama, dipertahankan, perlu untuk salin item RF, dan menyusutkan daftar dengan satu item NR kali. Semua waktu linier.
sumber
Pendekatan saya adalah saya pertama kali membuat daftar indeks, yang seharusnya dihapus. Setelah itu saya mengulangi indeks dan menghapus item dari daftar awal. Ini terlihat seperti ini:
sumber
Dalam C # satu cara mudah adalah dengan menandai yang ingin Anda hapus kemudian buat daftar baru untuk beralih ke ...
atau bahkan menggunakan linq ....
tetapi perlu dipertimbangkan jika tugas atau utas lain akan memiliki akses ke daftar yang sama pada saat yang sama Anda sibuk menghapus, dan mungkin menggunakan ConcurrentList sebagai gantinya.
sumber
Lacak elemen yang akan dihapus dengan properti, dan hapus semuanya setelah proses.
sumber
Hanya ingin menambahkan 2 sen saya untuk ini kalau-kalau ini membantu siapa pun, saya punya masalah yang sama tetapi perlu menghapus beberapa elemen dari daftar array saat itu sedang diulangi. jawaban tervvatif tertinggi melakukannya untuk saya sebagian besar sampai saya mengalami kesalahan dan menyadari bahwa indeks lebih besar dari ukuran daftar array dalam beberapa kasus karena beberapa elemen dihapus tetapi indeks loop tidak tetap melacak itu. Saya memperbaikinya dengan cek sederhana:
sumber
sumber
simples;
dilakukan di sini?