JavaRush /Java Blog /Random-IT /Rimozione di un elemento da un ArrayList in Java

Rimozione di un elemento da un ArrayList in Java

Pubblicato nel gruppo Random-IT
Ciao! Nell'ultima lezione abbiamo conosciuto la classe ArrayList e abbiamo anche imparato come eseguire con essa le operazioni più comuni. Inoltre, abbiamo identificato alcune differenze tra ArrayList e un array regolare. Ora esaminiamo la rimozione di un elemento dall'ArrayList. Abbiamo già detto che cancellare elementi di un array regolare non è molto conveniente. Rimuovere un elemento da un ArrayList - 1Poiché non possiamo eliminare la cella stessa, possiamo solo “azzerare” il suo valore:
public class Cat {

   private String name;

   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat[] cats = new Cat[3];
       cats[0] = new Cat("Thomas");
       cats[1] = new Cat("Hippopotamus");
       cats[2] = new Cat("Philip Markovich");

       cats[1] = null;

       System.out.println(Arrays.toString(cats));
   }


@Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               '}';
   }
}
Conclusione:

[Cat{name='Томас'}, null, Cat{name='Фorпп Маркович'}]
Ma quando viene ripristinato, rimane un "buco" nell'array. Non stiamo eliminando la cella, ma solo il suo contenuto. Immagina cosa succederebbe se avessimo un array di 50 gatti, 17 dei quali cancellati in questo modo. Avremo un array con 17 buche e ce ne prenderemo cura! Ricordare a memoria i numeri delle celle vuote in cui è possibile scrivere nuovi valori non è realistico. Se commetti un errore una volta, sovrascriverai la cella con il riferimento desiderato all'oggetto. Naturalmente c'è la possibilità di farlo con un po' più attenzione: dopo aver eliminato, sposta gli elementi dell'array all'inizio, in modo che il "buco" sia alla fine:
public static void main(String[] args) {

   Cat[] cats = new Cat[4];
   cats[0] = new Cat("Thomas");
   cats[1] = new Cat("Hippopotamus");
   cats[2] = new Cat("Philip Markovich");
   cats[3] = new Cat("Fluff");

   cats[1] = null;

   for (int i = 2; i < cats.length-1; i++) {
       //move the elements to the beginning so that the empty cell is at the end
       cats[i-1] = cats[i];
       cats[i] = null;
   }

   System.out.println(Arrays.toString(cats));
}
Conclusione:

[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}, null]
Ora sembra che la situazione migliori, ma difficilmente può essere definita una soluzione stabile. Come minimo, perché dovremo scrivere questo codice a mano ogni volta che rimuoviamo un elemento dall'array! Cattiva opzione. Potresti andare dall'altra parte e creare un metodo separato:
public void deleteCat(Cat[] cats, int indexToDelete) {
   //...remove the cat by index and shift the elements
}
Ma anche questo è di scarsa utilità: questo metodo può funzionare solo con gli oggetti Cat, ma non può funzionare con gli altri. Cioè, se nel programma ci sono altre 100 classi con le quali vogliamo utilizzare gli array, dovremo scrivere lo stesso metodo con esattamente la stessa logica in ognuna di esse. Questo è un completo fallimento -_- Ma nella classe ArrayList questo problema è stato risolto con successo! Implementa un metodo speciale per rimuovere elementi remove():
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Hippopotamus");
   Cat philipp = new Cat("Philip Markovich");
   Cat pushok = new Cat("Fluff");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);
   System.out.println(cats.toString());

   cats.remove(1);

   System.out.println(cats.toString());
}
Abbiamo passato l'indice del nostro oggetto al metodo ed è stato cancellato (proprio come in un array). Il metodo remove()ha due caratteristiche. Innanzitutto non lascia “buchi”. Implementa già la logica dello spostamento degli elementi quando si rimuove un elemento dal centro, che abbiamo precedentemente scritto a mano. Guarda l'output del codice precedente nella console:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
Abbiamo tolto un gatto dal centro e gli altri sono stati spostati in modo che non ci fossero spazi vuoti. In secondo luogo , può eliminare un oggetto non solo per indice (come un normale array), ma anche per riferimento all'oggetto :
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Hippopotamus");
   Cat philipp = new Cat("Philip Markovich");
   Cat pushok = new Cat("Fluff");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);
   System.out.println(cats.toString());

   cats.remove(philipp);

   System.out.println(cats.toString());
}
Conclusione:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
Questo può essere molto comodo se non vuoi tenere sempre in testa l’indice dell’oggetto desiderato. Sembra che abbiamo risolto la solita cancellazione. Ora immaginiamo questa situazione: vogliamo scorrere la nostra lista di elementi e rimuovere un gatto con un certo nome. Per questo utilizziamo uno speciale operatore di loop for- for each. Puoi saperne di più in questa lezione .
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Hippopotamus");
   Cat philipp = new Cat("Philip Markovich");
   Cat pushok = new Cat("Fluff");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);

   for (Cat cat: cats) {

       if (cat.name.equals("Hippopotamus")) {
           cats.remove(cat);
       }
   }

   System.out.println(cats);
}
Il codice sembra abbastanza logico. Tuttavia, il risultato potrebbe sorprenderti:

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
  at java.util.ArrayList$Itr.next(ArrayList.java:831)
  at Cat.main(Cat.java:25)
Una sorta di errore e non è chiaro il motivo per cui è apparso all'improvviso. Ci sono una serie di sfumature in questo processo che devono essere affrontate. Una regola generale che devi ricordare: non puoi scorrere una raccolta e modificarne gli elementi allo stesso tempo. Sì, sì, esattamente un cambiamento e non solo una cancellazione. Se provi nel nostro codice a sostituire la rimozione dei gatti con l'inserimento di nuovi, il risultato sarà lo stesso:
for (Cat cat: cats) {

   cats.add(new Cat("Salem Saberhegen"));
}

System.out.println(cats);

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
  at java.util.ArrayList$Itr.next(ArrayList.java:831)
  at Cat.main(Cat.java:25)
Abbiamo cambiato un'operazione con un'altra, ma il risultato non è cambiato: lo stesso errore ConcurrentModificationException. Si verifica proprio quando proviamo a infrangere una regola e a modificare l'elenco mentre lo iteriamo. In Java, per rimuovere elementi durante l'iterazione, è necessario utilizzare un oggetto speciale: un iteratore (classe Iterator). La classe Iteratorè responsabile di percorrere in sicurezza un elenco di elementi. È abbastanza semplice perché ha solo 3 metodi:
  • hasNext()- ritorna truea falseseconda che ci sia un elemento successivo nell'elenco o se abbiamo già raggiunto l'ultimo.
  • next()- restituisce l'elemento successivo dell'elenco
  • remove()- rimuove un elemento dalla lista
Come puoi vedere, l’iteratore è letteralmente “su misura” per le nostre esigenze e non c’è nulla di complicato in esso. Ad esempio, vogliamo verificare se la nostra lista contiene il seguente elemento e, in tal caso, stamparlo sulla console:
Iterator<Cat> catIterator = cats.iterator();//create an iterator
while(catIterator.hasNext()) {//as long as there are elements in the list

   Cat nextCat = catIterator.next();//get next element
   System.out.println(nextCat);// print it to the console
}
Conclusione:

Cat{name='Томас'}
Cat{name='Бегемот'}
Cat{name='Фorпп Маркович'}
Cat{name='Пушок'}
Come puoi vedere, la classe ArrayListimplementa già un metodo speciale per creare un iteratore - iterator(). Inoltre, tieni presente che quando creiamo un iteratore, specifichiamo la classe di oggetti con cui funzionerà ( <Cat>). In definitiva, possiamo risolvere facilmente il nostro problema originale utilizzando un iteratore. Ad esempio, eliminiamo un gatto chiamato "Philip Markovich":
Iterator<Cat> catIterator = cats.iterator();//create an iterator
while(catIterator.hasNext()) {//as long as there are elements in the list

   Cat nextCat = catIterator.next();//get next element
   if (nextCat.name.equals("Philip Markovich")) {
       catIterator.remove();//delete the cat with the desired name
   }
}

System.out.println(cats);
Conclusione:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
Potresti aver notato che non abbiamo specificato né l'indice dell'elemento né il nome della variabile di riferimento nel metodo iteratore remove()! L'iteratore è più intelligente di quanto possa sembrare: il metodo remove()rimuove l'ultimo elemento restituito dall'iteratore. Come puoi vedere, ha funzionato esattamente come necessario :) Questo è praticamente tutto ciò che devi sapere sulla rimozione di elementi da ArrayList. Più precisamente, quasi tutto. Nella prossima lezione esamineremo gli “interni” di questa classe e vedremo cosa succede lì durante le operazioni :) Ci vediamo!
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION