Autor Wątek: Usuwanie elementów z kolekcji.  (Przeczytany 6421 razy)

Offline maze

  • Użytkownik

# Luty 22, 2011, 06:04:34
Witam, napotykam na pewien problem przy usuwaniu elementów z kolekcji. Stosując poniższy kod kompilator wyrzuca mi wyjątek. Jeżeli ktoś wie co robię nie tak, to będę wdzięczny za jakiekolwiek informacje.

List<MojaKlasa> lista = new List<MojaKlasa>();

foreach (MojaKlasa element in lista)
{
     if (element.IsDead == true)
     {
          lista.RemoveAt(lista.IndexOf(element));
     }
}

Próbowałem jeszcze w ten sposób, ale też nie zadziałało.

List<MojaKlasa> lista = new List<MojaKlasa>();
List<int> toRemoveList = new List<int>();
var toRemove =  from x in lista where x.IsDead == true select x;
                                   
foreach(MojaKlasa element in toRemove)
{
     toRemoveList.Add(lista.IndexOf(element));
}
foreach (int index in toRemove)
{
     lista.RemoveAt(index);
}
« Ostatnia zmiana: Luty 23, 2011, 03:27:33 wysłana przez maze »

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline krajew4

  • Użytkownik

# Luty 22, 2011, 08:22:47
Jak iterujesz foreachem po kolekcji to nie możesz jej w trakcie modyfikować. (wydaje się logiczne)
Zamień te pętle na zwykłą for z indeksem i pamiętaj, aby iterować z maksymalnej wartości do 0 jak chcesz usuwać a nie odwrotnie.

Offline Kos

  • Użytkownik
    • kos.gd

# Luty 22, 2011, 10:16:18
i pamiętaj, aby iterować z maksymalnej wartości do 0 jak chcesz usuwać a nie odwrotnie.

?

Offline Reg

  • Administrator
    • Adam Sawicki - Home Page

# Luty 22, 2011, 11:44:01
Kiedy usuwasz z listy element o danym indeksie, wszystkie dalsze przesuwają się o jedno miejsce wcześniej i w ten sposób reszta kolekcji już nie wygląda tak samo. Dlatego najlepiej przechodzić ją od końca do początku. Usuwanie o które pytasz proponuję zrobić tak:

// Przechodzi indeksy Length-1, Length-2, ..., 0
for (int i = lista.Length; i--; )
{
     if (lista[i].IsDead == true)
     {
          lista.RemoveAt(i);
     }
}

Offline Xion

  • Moderator
    • xion.log

# Luty 22, 2011, 12:17:54
Można też przechodzić kolekcję od początku, ale przesuwać się do przodu tylko wtedy, gdy nie usuwamy elementu:
for (int i = 0; i < lista.Length; ) {
    if (lista[i].IsDead) lista.RemoveAt(i);
    else ++i;

Offline Kos

  • Użytkownik
    • kos.gd

# Luty 22, 2011, 14:53:37
Można też przechodzić kolekcję od początku, ale przesuwać się do przodu tylko wtedy, gdy nie usuwamy elementu:
for (int i = 0; i < lista.Length; ) {
    if (lista[i].IsDead) lista.RemoveAt(i);
    else ++i;

Tak. A jeśli iterujemy po wektorze z nieistotną kolejnością, to możemy zastąpić usuwany element ostatnim i zmniejszyć długość listy o 1, by nie przesuwać całego ogona co usunięcie.

Offline soku11

  • Użytkownik

# Luty 22, 2011, 16:27:31
Lub po prostu:
lista.RemoveAll( e => e.IsDead);

Pozdrawiam.

Offline maze

  • Użytkownik

# Luty 23, 2011, 03:31:21
Jak iterujesz foreachem po kolekcji to nie możesz jej w trakcie modyfikować. (wydaje się logiczne)
Zamień te pętle na zwykłą for z indeksem i pamiętaj, aby iterować z maksymalnej wartości do 0 jak chcesz usuwać a nie odwrotnie.

No fakt, dziwie się, że dopiero teraz to do mnie dotarło.
Lub po prostu:
lista.RemoveAll( e => e.IsDead);

Pozdrawiam.

W tenże sposób właśnie to rozwiązałem, jednak nie wszystkie generyki posiadają metodę RemoveAll().

Dziękuje wszystkim za szybką odpowiedź.