Я ищу лучший шаблон для работы со списком элементов, каждый из которых необходимо обработать, а затем в зависимости от результата удаляются из списка.
Вы не можете использовать .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
.
Есть ли элегантный способ сделать это?
list.RemoveAll(Function(item) item.Value = somevalue)
.size()
вместо.Count
и.remove(i)
вместо.removeAt(i)
. Clever - спасибо!RemoveAll()
занимает в три раза больше времени, чем обратныйfor
цикл. Так что я определенно придерживаюсь цикла, по крайней мере, в тех разделах, где это важно.Простое и понятное решение:
Используйте стандартный цикл for, работающий задом наперед на вашей коллекции и
RemoveAt(i)
удаляющий элементы.источник
Обратная итерация должна быть первой вещью, которая приходит на ум, когда вы хотите удалить элементы из Коллекции во время итерации по ней.
К счастью, есть более элегантное решение, чем написание цикла for, которое включает в себя ненужную типизацию и может быть подвержено ошибкам.
источник
Reverse<T>()
создает итератор, который просматривает список в обратном направлении, но выделяет для него дополнительный буфер того же размера, что и сам список ( referenceource.microsoft.com/#System.Core/System/Linq/… ).Reverse<T>
не просто просматривать исходный список в обратном порядке (без выделения дополнительной памяти). Поэтому обаToList()
иReverse()
имеют одинаковое потребление памяти (оба создают копию), ноToList()
ничего не делают с данными. СReverse<int>()
, я хотел бы знать, почему список перевернут, по какой причине.Reverse<T>()
создает новый буфер, я не совсем уверен, что понимаю, почему это необходимо. Мне кажется, что в зависимости от базовой структурыEnumerable
, по крайней мере, в некоторых случаях должно быть возможно выполнить обратную итерацию без выделения линейной памяти.Если вы добавите «.ToList ()» в свой список (или результаты запроса LINQ), вы сможете удалить «элемент» непосредственно из «списка» без изменения «ужасной» коллекции; операция перечисления может не выполняться . » ошибка. Компилятор создает копию «списка», чтобы вы могли безопасно выполнять удаление массива.
Хотя этот шаблон не очень эффективен, он имеет естественный вид и достаточно гибок практически для любой ситуации . Например, когда вы хотите сохранить каждый «элемент» в БД и удалить его из списка только тогда, когда сохранение БД выполнено успешно.
источник
Выберите элементы, которые вы делаете хотите, вместо того, чтобы пытаться удалить элементы, которые вы не хотите. Это намного проще (и вообще более эффективно), чем удаление элементов.
Я хотел опубликовать это как комментарий в ответ на комментарий, оставленный Майклом Диллоном ниже, но это слишком долго и, вероятно, полезно в моем ответе в любом случае:
Лично я никогда не удаляю элементы по одному, если вам нужно удалить, а затем позвонить
RemoveAll
который принимает предикат и только один раз переставляет внутренний массив, тогда какRemove
выполняетArray.Copy
операцию для каждого удаляемого элемента.RemoveAll
значительно эффективнееИ когда вы перебираете список в обратном направлении, у вас уже есть индекс элемента, который вы хотите удалить, поэтому было бы гораздо эффективнее вызвать его
RemoveAt
, потому чтоRemove
сначала выполняется обход списка, чтобы найти индекс элемента, который вы хотите удалить. Пытаюсь удалить, но вы уже знаете этот индекс.Итак, в общем, я не вижу никакой причины когда-либо вызывать
Remove
цикл for. И в идеале, если это вообще возможно, используйте приведенный выше код для потоковой передачи элементов из списка по мере необходимости, чтобы вообще не нужно было создавать вторую структуру данных.источник
Использование ToArray () в общем списке позволяет вам удалить (элемент) из общего списка:
источник
Использование .ToList () создаст копию вашего списка, как объясняется в этом вопросе: ToList () - Создает ли он новый список?
Используя ToList (), вы можете удалить из исходного списка, потому что вы на самом деле перебираете копию.
источник
Если функция, определяющая, какие элементы для удаления, не имеет побочных эффектов и не мутирует элемент (это чистая функция), простое и эффективное решение (с линейным временем):
Если есть побочные эффекты, я бы использовал что-то вроде:
Это все еще линейное время, при условии, что хеш хорош. Но он имеет увеличенное использование памяти из-за хэш-набора.
Наконец, если ваш список - только
IList<T>
вместо,List<T>
я предлагаю мой ответ на Как я могу сделать этот специальный итератор foreach? , Это будет иметь линейное время выполнения с учетом типичных реализацийIList<T>
, по сравнению с квадратичным временем выполнения многих других ответов.источник
Поскольку любое удаление берется при условии, что вы можете использовать
источник
источник
Вы не можете использовать foreach, но вы можете выполнять итерацию вперед и управлять переменной индекса цикла при удалении элемента, например, так:
Обратите внимание, что в целом все эти методы основаны на поведении итерируемой коллекции. Техника, показанная здесь, будет работать со стандартным списком (T). (Вполне возможно написать свой собственный класс коллекции и итератор, который позволяет удалять элементы во время цикла foreach.)
источник
Использование
Remove
илиRemoveAt
в списке во время итерации по этому списку было намеренно затруднено, потому что это почти всегда неправильно . Возможно, вам удастся заставить его работать с какой-то хитрой уловкой, но это будет очень медленно. Каждый раз, когда вы звоните,Remove
он должен просмотреть весь список, чтобы найти элемент, который вы хотите удалить. Каждый раз, когда вы звоните,RemoveAt
он должен перемещать последующие элементы на 1 позицию влево. Таким образом, любое решение, использующееRemove
илиRemoveAt
, потребовало бы квадратичного времени O (n²) .Используйте,
RemoveAll
если можете. В противном случае следующий шаблон отфильтрует список на месте за линейное время O (n) .источник
Я бы хотел, чтобы «шаблон» был примерно таким:
Это позволит привести код в соответствие с процессом, который происходит в мозгу программиста.
источник
Nullable<bool>
, если вы хотите разрешить немаркированные), а затем используйте его после foreach для удаления / сохранения элементов.Предполагая, что предикат является логическим свойством элемента, что, если оно истинно, элемент должен быть удален:
источник
Я бы переназначил список из запроса LINQ, который отфильтровал элементы, которые вы не хотели сохранять.
Если список не очень большой, при этом не должно быть серьезных проблем с производительностью.
источник
Лучший способ удалить элементы из списка во время его перебора - использовать
RemoveAll()
. Но основная проблема, которую пишут люди, заключается в том, что они должны делать некоторые сложные вещи внутри цикла и / или иметь сложные случаи сравнения.Решение по-прежнему использовать,
RemoveAll()
но использовать эту запись:источник
Просто создайте совершенно новый список из первого. Я говорю «Легко», а не «Правильно», поскольку создание совершенно нового списка, вероятно, приводит к выигрышу в производительности по сравнению с предыдущим методом (я не удосужился провести сравнительный анализ). Я обычно предпочитаю этот шаблон, он также может быть полезен при преодолении Ограничения Linq-To-Entities.
Этот способ циклически перебирает список в обратном направлении с помощью простого старого цикла For. Делать это вперед может быть проблематично, если размер коллекции изменяется, но в обратном направлении всегда должно быть безопасно.
источник
Скопируйте список, который вы повторяете. Затем удалите из копии и внесите оригинал. Возвращение назад сбивает с толку и не работает хорошо при параллельном цикле.
источник
Я бы так сделал
ВЫВОД
источник
Я оказался в похожей ситуации, когда мне пришлось удалить каждый n- й элемент в заданном
List<T>
.источник
Стоимость удаления элемента из списка пропорциональна количеству элементов, следующих за одним, подлежащим удалению. В случае, когда первая половина элементов соответствует критериям удаления, любой подход, основанный на удалении элементов по отдельности, в конечном итоге должен будет выполнять около N * N / 4 операций копирования элементов, что может оказаться очень дорогим, если список большой ,
Более быстрый подход заключается в сканировании списка, чтобы найти первый удаляемый элемент (если таковой имеется), а затем с этого момента копировать каждый элемент, который должен быть сохранен в том месте, где он принадлежит. Как только это будет сделано, если R элементов должны быть сохранены, первыми элементами R в списке будут эти элементы R, и все элементы, требующие удаления, будут в конце. Если эти элементы будут удалены в обратном порядке, системе не придется копировать какой-либо из них, поэтому, если в списке было N элементов, из которых R элементов, включая все первые F, были сохранены, необходимо будет скопируйте элементы RF и сократите список на один элемент NR раз. Все линейное время.
источник
Мой подход заключается в том, что я сначала создаю список индексов, которые должны быть удалены. После этого я перебираю индексы и удаляю элементы из первоначального списка. Это выглядит так:
источник
В C # один простой способ - пометить те, которые вы хотите удалить, а затем создать новый список для повторения ...
или еще проще использовать linq ....
но стоит подумать, будут ли другие задачи или потоки иметь доступ к тому же списку в то же время, когда вы заняты удалением, и, возможно, вместо этого использовать ConcurrentList.
источник
Проследите элементы, которые должны быть удалены со свойством, и удалите их все после процесса.
источник
Я просто хотел добавить свои 2 цента к этому на случай, если это кому-нибудь поможет, у меня была похожая проблема, но мне нужно было удалить несколько элементов из списка массивов, пока он повторялся. ответ с наибольшим числом голосов делал это для меня по большей части, пока я не столкнулся с ошибками и не понял, что в некоторых случаях индекс превышал размер списка массивов, поскольку удалялось несколько элементов, но индекс цикла не сохранялся отслеживать это. Я исправил это с помощью простой проверки:
источник
источник
simples;
здесь делать?