Estou procurando um padrão melhor para trabalhar com uma lista de elementos que cada um precisa ser processado e, dependendo do resultado, serão removidos da lista.
Você não pode usar .Remove(element)
dentro de um foreach (var element in X)
(porque resulta em Collection was modified; enumeration operation may not execute.
exceção) ... você também não pode usar for (int i = 0; i < elements.Count(); i++)
e .RemoveAt(i)
porque isso interrompe sua posição atual na coleção em relação a i
.
Existe uma maneira elegante de fazer isso?
list.RemoveAll(Function(item) item.Value = somevalue)
.size()
vez de.Count
e em.remove(i)
vez de.removeAt(i)
. Inteligente - obrigado!RemoveAll()
leva três vezes mais tempo que ofor
loop reverso. Então, eu definitivamente estou aderindo ao loop, pelo menos nas seções onde isso importa.Uma solução simples e direta:
Use um loop for padrão correndo para trás em sua coleção e
RemoveAt(i)
para remover elementos.fonte
A iteração reversa deve ser a primeira coisa a se lembrar quando você deseja remover elementos de uma coleção enquanto itera sobre ela.
Felizmente, existe uma solução mais elegante do que escrever um loop for, que envolve digitação desnecessária e pode estar sujeito a erros.
fonte
Reverse<T>()
cria um iterador que percorre a lista de trás para frente, mas aloca buffer extra com o mesmo tamanho da lista em si ( referenceource.microsoft.com/#System.Core/System/Linq/… ).Reverse<T>
não passa apenas pela lista original na ordem inversa (sem alocar memória extra). Portanto, ambosToList()
eReverse()
têm o mesmo consumo de memória (ambos criam cópia), masToList()
não fazem nada com os dados. ComReverse<int>()
, gostaria de saber por que a lista é revertida, por que motivo.Reverse<T>()
cria um novo buffer, não tenho muita certeza de entender por que isso é necessário. Parece-me que, dependendo da estrutura subjacente daEnumerable
, pelo menos em alguns casos deve ser possível obter a iteração reversa sem alocar memória linear.Se você adicionar ".ToList ()" à sua lista (ou aos resultados de uma consulta LINQ), poderá remover o "item" diretamente da "lista" sem a temida " Coleção modificada; a operação de enumeração pode não ser executada ". erro. O compilador faz uma cópia da "lista", para que você possa remover com segurança a matriz.
Embora esse padrão não seja super eficiente, ele tem uma sensação natural e é flexível o suficiente para quase qualquer situação . Por exemplo, quando você deseja salvar cada "item" em um banco de dados e removê-lo da lista somente quando o salvamento do banco de dados for bem-sucedido.
fonte
Selecione os elementos que você não quer, em vez de tentar remover os elementos que você não quer. Isso é muito mais fácil (e geralmente mais eficiente também) do que remover elementos.
Eu queria postar isso como um comentário em resposta ao comentário deixado por Michael Dillon abaixo, mas é muito longo e provavelmente útil para ter em minha resposta de qualquer maneira:
Pessoalmente, eu nunca removeria itens um por um, se você precisar removê-los, em seguida, chamar
RemoveAll
que leva um predicado e reorganiza a matriz interna apenas uma vez, enquantoRemove
faz umaArray.Copy
operação para cada elemento que você remove.RemoveAll
é muito mais eficiente.E quando você está repetindo a iteração sobre uma lista, você já possui o índice do elemento que deseja remover, portanto seria muito mais eficiente chamar
RemoveAt
, porqueRemove
primeiro é percorrida a lista para encontrar o índice do elemento que você deseja remover. está tentando remover, mas você já conhece esse índice.Então, apesar de tudo, não vejo motivo para chamar
Remove
um loop for. E, idealmente, se for possível, use o código acima para transmitir elementos da lista conforme necessário, para que nenhuma segunda estrutura de dados tenha que ser criada.fonte
O uso do ToArray () em uma lista genérica permite que você remova (item) na sua lista genérica:
fonte
O uso de .ToList () fará uma cópia da sua lista, conforme explicado nesta pergunta: ToList () - Cria uma nova lista?
Usando ToList (), você pode remover da sua lista original, porque na verdade está iterando sobre uma cópia.
fonte
Se a função que determina quais itens excluir não tem efeitos colaterais e não altera o item (é uma função pura), uma solução simples e eficiente (tempo linear) é:
Se houver efeitos colaterais, eu usaria algo como:
Ainda é um tempo linear, assumindo que o hash seja bom. Mas tem um aumento no uso de memória devido ao hashset.
Finalmente, se sua lista é apenas um em
IList<T>
vez de umList<T>
, sugiro minha resposta para Como posso fazer esse iterador especial para cada um? . Isso terá um tempo de execução linear, considerando as implementações típicas deIList<T>
, em comparação com o tempo de execução quadrático de muitas outras respostas.fonte
Como qualquer remoção é feita sob uma condição, você pode usar
fonte
fonte
Você não pode usar o foreach, mas pode iterar para frente e gerenciar sua variável de índice de loop ao remover um item, assim:
Observe que, em geral, todas essas técnicas dependem do comportamento da coleção que está sendo iterada. A técnica mostrada aqui funcionará com a lista padrão (T). (É bem possível escrever sua própria classe de coleção e iterator que faz permitir a remoção item durante um loop foreach.)
fonte
O uso
Remove
ouRemoveAt
em uma lista durante a iteração sobre essa lista foi intencionalmente difícil, porque quase sempre é a coisa errada a se fazer . Você pode fazê-lo funcionar com algum truque inteligente, mas seria extremamente lento. Toda vez que você ligar,Remove
ele deve percorrer toda a lista para encontrar o elemento que você deseja remover. Toda vez que você ligar,RemoveAt
ele deve mover os elementos subsequentes 1 posição para a esquerda. Como tal, qualquer solução que useRemove
ouRemoveAt
, exigiria tempo quadrático, O (n²) .Use
RemoveAll
se puder. Caso contrário, o padrão a seguir filtrará a lista no local em tempo linear, O (n) .fonte
Eu gostaria que o "padrão" fosse algo assim:
Isso alinharia o código com o processo que ocorre no cérebro do programador.
fonte
Nullable<bool>
, se você deseja permitir não marcado), depois use-o após o foreach para remover / manter itens.Assumindo que predicado é uma propriedade booleana de um elemento, que, se for verdadeiro, o elemento deve ser removido:
fonte
Eu reatribuiria a lista a partir de uma consulta LINQ que filtrasse os elementos que você não queria manter.
A menos que a lista seja muito grande, não deve haver problemas significativos de desempenho ao fazer isso.
fonte
A melhor maneira de remover itens de uma lista durante a iteração é usando
RemoveAll()
. Mas a principal preocupação escrita pelas pessoas é que elas precisam fazer algumas coisas complexas dentro do loop e / ou ter casos complexos de comparação.A solução ainda é usar,
RemoveAll()
mas use esta notação:fonte
Basta criar uma lista totalmente nova a partir da primeira. Digo "Fácil" ao invés de "Certo", pois a criação de uma lista totalmente nova provavelmente tem um prêmio de desempenho em relação ao método anterior (não me importei com nenhum benchmarking). Geralmente prefiro esse padrão, mas também pode ser útil para superar Limitações do Linq-To-Entities.
Dessa maneira, percorre a lista para trás com um loop For simples e antigo. Fazer isso adiante pode ser problemático se o tamanho da coleção for alterado, mas sempre deve ser seguro.
fonte
Copie a lista que você está iterando. Em seguida, retire da cópia e interaja com o original. Retroceder é confuso e não funciona bem ao fazer um loop paralelo.
fonte
Eu faria assim
RESULTADO
fonte
Eu me encontrei em uma situação semelhante, onde eu tinha que remover todos os n º elemento em uma determinada
List<T>
.fonte
O custo de remover um item da lista é proporcional ao número de itens após o item a ser removido. No caso em que a primeira metade dos itens se qualifica para remoção, qualquer abordagem baseada na remoção de itens individualmente terá que executar cerca de N * N / 4 operações de cópia de itens, que podem ficar muito caras se a lista for grande .
Uma abordagem mais rápida é percorrer a lista para encontrar o primeiro item a ser removido (se houver) e, a partir desse momento, copiar cada item que deve ser mantido no local a que pertence. Uma vez feito isso, se os itens R devem ser retidos, os primeiros itens R na lista serão esses itens R e todos os itens que requerem exclusão estarão no final. Se esses itens forem excluídos na ordem inversa, o sistema não precisará copiar nenhum deles; portanto, se a lista tiver N itens dos quais itens R, incluindo todos os primeiros F, foram retidos, será necessário copie itens de RF e reduza a lista por um item NR vezes. Todo o tempo linear.
fonte
Minha abordagem é que primeiro crie uma lista de índices, que devem ser excluídos. Depois, percorro os índices e removo os itens da lista inicial. É assim:
fonte
Em C #, uma maneira fácil é marcar as que você deseja excluir e criar uma nova lista para repetir ...
ou ainda mais simples usar linq ....
mas vale a pena considerar se outras tarefas ou threads terão acesso à mesma lista ao mesmo tempo em que você estiver removendo e, talvez, use uma ConcurrentList.
fonte
Rastreie os elementos a serem removidos com uma propriedade e remova todos eles após o processo.
fonte
Só queria adicionar meus 2 centavos a isso, caso isso ajude alguém, eu tive um problema semelhante, mas precisava remover vários elementos de uma lista de matriz enquanto ela estava sendo repetida. a resposta mais votada mais alta fez por mim a maior parte do tempo, até que encontrei erros e percebi que o índice era maior do que o tamanho da lista de matrizes em alguns casos porque vários elementos estavam sendo removidos, mas o índice do loop não foi mantido. faixa disso. Corrigi isso com uma verificação simples:
fonte
fonte
simples;
faz aqui?