CodeGym /Java Blog /Random-IT /Eliminazione di un elemento da un ArrayList
John Squirrels
Livello 41
San Francisco

Eliminazione di un elemento da un ArrayList

Pubblicato nel gruppo Random-IT
CIAO! Nell'ultima lezione abbiamo conosciuto la ArrayListclasse e abbiamo imparato come eseguire le operazioni più comuni con questa classe. Inoltre, abbiamo evidenziato diverse differenze tra un array ArrayListe un array ordinario. Ma abbiamo aggirato un argomento, ovvero come eliminare elementi da un file ArrayList. Ne discuteremo ora. Eliminazione di un elemento da un ArrayList - 1Abbiamo già accennato al fatto che l'eliminazione di elementi da un normale array non è molto conveniente. Poiché non possiamo eliminare l'elemento stesso, possiamo solo "azzerare" (impostare su null) 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("Behemoth");
       cats[2] = new Cat("Lionel Messi");

       cats[1] = null;

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

   
@Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               '}';
   }
}
Output: [Cat{name='Thomas'}, null, Cat{name='Lionel Messi'}] Ma l'impostazione di un elemento dell'array su null lascia un "buco". Non abbiamo rimosso la posizione nell'array, solo il suo contenuto. Immagina cosa succederebbe se avessimo una schiera di 50 gatti e ne rimuovessimo 17 in questo modo. Avremo un array con 17 fori. Prova a tenerne traccia! Non è realistico aspettarsi di ricordare il numero di celle vuote in cui è possibile scrivere nuovi valori. Se commetti un errore, sovrascriverai un riferimento a un oggetto che desideri. C'è, ovviamente, un modo per farlo un po' più attentamente: dopo aver rimosso un elemento, sposta gli elementi nella parte anteriore dell'array per inserire il "buco" alla fine:

public static void main(String[] args) {

   Cat[] cats = new Cat[4];
   cats[0] = new Cat("Thomas");
   cats[1] = new Cat("Behemoth");
   cats[2] = new Cat("Lionel Messi");
   cats[2] = new Cat("Fluffy");

   cats[1] = null;

   for (int i = 2; i < cats.length-1; i++) {
       cats [i-1] = cats [i];// Move the elements to the front of the array, so the empty position is at the end
   }

   System.out.println(Arrays.toString(cats));
}
Output: [Cat{name='Thomas'}, Cat{name='Fluffy'}, Cat{name='Fluffy'}, null] Sembra migliore, ma difficilmente può essere definita una soluzione robusta. Se non altro per il fatto che dobbiamo scrivere questo codice ogni volta che cancelliamo un elemento da un array! Questa è una cattiva opzione. Potremmo andare in un altro modo e creare un metodo separato:

public void deleteCat(Cat[] cats, int indexToDelete) {
   //...delete the cat corresponding to the index and move the elements
}
Ma anche questo serve a poco: questo metodo può funzionare solo con Catoggetti, ma non con altri tipi. In altre parole, se un programma ha altre 100 classi che vogliamo utilizzare con gli array, dovremo scrivere lo stesso metodo con esattamente la stessa logica in ciascuna di esse. Questo è un disastro totale -_- Ma la ArrayListclasse risolve questo problema! Implementa un metodo speciale per rimuovere gli elementi:remove()

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);
   System.out.println(cats.toString());

   cats.remove(1);

   System.out.println(cats.toString());
}
Passiamo l'indice del nostro oggetto al metodo, che lo cancella (proprio come in un array). Il remove()metodo ha due caratteristiche speciali. Innanzitutto, non lascia "buchi". Implementa già la logica necessaria per spostare gli elementi quando un elemento viene rimosso dal centro, che abbiamo precedentemente scritto noi stessi. Guarda l'output del codice precedente:

[Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]

[Cat{name='Thomas'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
Abbiamo rimosso un gatto dal centro e gli altri sono stati spostati in modo che non ci fossero spazi vuoti. In secondo luogo , può eliminare oggetti non solo per indice (come un normale array), ma anche per riferimento :

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);
   System.out.println(cats.toString());

   cats.remove(lionel);

   System.out.println(cats.toString());
}
Output: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}] [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] Questo può essere molto comodo se non vuoi tenere sempre traccia dell'indice dell'oggetto desiderato. Sembra che abbiamo capito la cancellazione ordinaria. Ora immaginiamo questa situazione: vogliamo iterare sulla nostra lista e rimuovere un gatto con un nome specifico . Per fare questo, useremo un forciclo veloce (chiamato anche ciclo for-each), che ci è stato presentato nelle lezioni di Rishi:

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);

   for (Cat cat: cats) {

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

   System.out.println(cats);
}
Il codice sembra perfettamente logico. Ma il risultato potrebbe essere una grande sorpresa: eccezione nel thread "main" java.util.ConcurrentModificationException in java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) in java.util.ArrayList$Itr.next(ArrayList. java:831) in Cat.main(Cat.java:25) C'è una sorta di errore e non è chiaro il motivo per cui si è verificato. Questo processo comporta una serie di sfumature che devono essere affrontate. Ecco la regola generale che devi ricordare: non puoi iterare contemporaneamente su una raccolta e modificarne gli elementi. E intendiamo qualsiasi tipo di cambiamento, non semplicemente rimozione. Se sostituisci la rimozione del gatto con un tentativo di inserire nuovi gatti, il risultato sarà lo stesso:

for (Cat cat: cats) {

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

System.out.println(cats);
Eccezione nel thread "principale" java.util.ConcurrentModificationException in java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) in java.util.ArrayList$Itr.next(ArrayList.java:831) in Cat.main( Cat.java:25) Abbiamo cambiato un'operazione in un'altra, ma il risultato non è cambiato: otteniamo la stessa ConcurrentModificationException . Si verifica proprio quando proviamo a infrangere la regola precedente modificando l'elenco durante l'iterazione su di esso. In Java, abbiamo bisogno di un oggetto speciale chiamato iteratore (Iteratorclasse) per eliminare gli elementi durante l'iterazione su una raccolta. LaIteratorclasse è responsabile dell'iterazione sicura dell'elenco di elementi. È abbastanza semplice, poiché ha solo 3 metodi:
  • hasNext()- restituisce vero o falso, a seconda che ci sia un elemento successivo nell'elenco o che siamo già arrivati ​​all'ultimo.
  • next()- restituisce l'elemento successivo nell'elenco
  • remove()- rimuove un elemento dall'elenco
Come puoi vedere, l'iteratore è fatto su misura per le nostre esigenze e allo stesso tempo non c'è niente di complicato. Supponiamo di voler controllare se c'è un elemento successivo nella nostra lista e visualizzarlo se c'è:

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 the next element
   System.out.println(nextCat);// Display it
}
Output: Cat{name='Thomas'} Cat{name='Behemoth'} Cat{name='Lionel Messi'} Cat{name='Fluffy'} Come puoi vedere, ha già implementato un metodo ArrayListspeciale per creare un iteratore: iterator(). Inoltre, si noti che quando creiamo un iteratore, specifichiamo la classe di oggetti con cui funzionerà ( <Cat>). La linea di fondo è che un iteratore gestisce facilmente il nostro compito originale. Ad esempio, rimuovi il gatto chiamato "Lionel Messi":

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 the next element
   if (nextCat.name.equals("Lionel Messi")) {
       catIterator.remove();// Delete the cat with the specified name
   }
}

System.out.println(cats);
Output: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}]remove() Potresti aver notato che non abbiamo specificato né l'indice né il nome nel metodo dell'iteratore ! L'iteratore è più intelligente di quanto possa sembrare: remove()rimuove l'ultimo elemento restituito dall'iteratore. Come puoi vedere, ha fatto proprio quello che volevamo che facesse :) In linea di principio, questo è tutto ciò che devi sapere sulla rimozione di elementi da un file ArrayList. Beh, quasi tutto. Nella prossima lezione, daremo un'occhiata all'interno di questa classe e vedremo cosa succede lì durante le varie chiamate di metodo :) Fino ad allora!
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION