CodeGym /Blog Java /Random-FR /Supprimer un élément d'une ArrayList
Auteur
Volodymyr Portianko
Java Engineer at Playtika

Supprimer un élément d'une ArrayList

Publié dans le groupe Random-FR
Salut! Dans la dernière leçon, nous nous sommes familiarisés avec la ArrayListclasse et avons appris à effectuer les opérations les plus courantes avec cette classe. De plus, nous avons souligné plusieurs différences entre un ArrayListet un tableau ordinaire. Mais nous avons contourné un sujet, à savoir comment supprimer des éléments d'un fichier ArrayList. Nous allons en discuter maintenant. Supprimer un élément d'une ArrayList - 1Nous avons déjà mentionné que la suppression d'éléments d'un tableau ordinaire n'est pas très pratique. Comme nous ne pouvons pas supprimer l'élément lui-même, nous ne pouvons que "mettre à zéro" (mettre à zéro) sa valeur :

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 + '\'' +
               '}';
   }
}
Sortie : [Cat{name='Thomas'}, null, Cat{name='Lionel Messi'}] Mais définir un élément de tableau sur null laisse un "trou". Nous n'avons pas supprimé la position dans le tableau, seulement son contenu. Imaginez ce qui se passerait si nous avions un ensemble de 50 chats et en supprimions 17 de cette façon. Nous aurons un tableau avec 17 trous. Essayez simplement de garder une trace d'eux! Il n'est pas réaliste de s'attendre à se souvenir du nombre de cellules vides où vous pouvez écrire de nouvelles valeurs. Si vous faites une erreur, vous écraserez une référence d'objet que vous voulez. Il existe bien sûr un moyen de faire cela avec un peu plus de précaution : après avoir supprimé un élément, déplacez les éléments vers l'avant du tableau pour mettre le "trou" à la fin :

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));
}
Sortie : [Cat{name='Thomas'}, Cat{name='Fluffy'}, Cat{name='Fluffy'}, null] Cela semble mieux, mais on peut difficilement l'appeler une solution robuste. Si ce n'est pour aucune autre raison que le fait que nous devons écrire ce code à chaque fois que nous supprimons un élément d'un tableau ! C'est une mauvaise option. Nous pourrions suivre une autre voie et créer une méthode distincte :

public void deleteCat(Cat[] cats, int indexToDelete) {
   //...delete the cat corresponding to the index and move the elements
}
Mais c'est aussi peu utile : cette méthode ne peut fonctionner qu'avec Catdes objets, mais pas avec d'autres types. En d'autres termes, si un programme a 100 autres classes que nous voulons utiliser avec des tableaux, nous devrons écrire la même méthode avec exactement la même logique dans chacune d'elles. C'est un désastre total -_- Mais la ArrayListclasse résout ce problème ! Il implémente une méthode spéciale pour supprimer des éléments :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());
}
Nous passons l'index de notre objet à la méthode, qui le supprime (comme dans un tableau). La remove()méthode a deux particularités. Tout d'abord, il ne laisse pas de "trous". Il implémente déjà la logique nécessaire pour déplacer des éléments lorsqu'un élément est retiré du milieu, que nous avons précédemment écrit nous-mêmes. Regardez la sortie du code précédent :

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

[Cat{name='Thomas'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
Nous avons retiré un chat du milieu et les autres ont été déplacés afin qu'il n'y ait pas d'espaces vides. Deuxièmement , il peut supprimer des objets non seulement par index (comme un tableau normal), mais aussi par référence :

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());
}
Sortie : [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}] [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] Cela peut être très pratique si vous ne voulez pas toujours garder une trace de l'index de l'objet souhaité. Il semble que nous ayons compris la suppression ordinaire. Imaginons maintenant cette situation : nous voulons parcourir notre liste et supprimer un chat avec un nom spécifique . Pour ce faire, nous allons utiliser une forboucle rapide (également appelée boucle for-each), qui nous a été présentée dans les leçons de 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);
}
Le code semble parfaitement logique. Mais le résultat pourrait être une grande surprise : Exception dans le thread "principal" java.util.ConcurrentModificationException à java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) à java.util.ArrayList$Itr.next(ArrayList. java:831) sur Cat.main(Cat.java:25) Il y a une sorte d'erreur, et on ne sait pas pourquoi elle s'est produite. Ce processus implique un certain nombre de nuances qui doivent être prises en compte. Voici la règle générale dont vous devez vous souvenir : vous ne pouvez pas parcourir simultanément une collection et modifier ses éléments. Et nous entendons toute sorte de changement, pas simplement le retrait. Si vous remplacez la suppression du chat par une tentative d'insertion de nouveaux chats, le résultat sera le même :

for (Cat cat: cats) {

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

System.out.println(cats);
Exception dans le thread "principal" java.util.ConcurrentModificationException à java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) à java.util.ArrayList$Itr.next(ArrayList.java:831) à Cat.main( Cat.java:25) Nous avons changé une opération en une autre, mais le résultat n'a pas changé : nous obtenons la même ConcurrentModificationException . Cela se produit précisément lorsque nous essayons d'enfreindre la règle ci-dessus en modifiant la liste tout en parcourant celle-ci. En Java, nous avons besoin d'un objet spécial appelé un itérateur (Iteratorclasse) pour supprimer des éléments lors de l'itération sur une collection. LaIteratorclasse est chargée d'itérer en toute sécurité sur la liste des éléments. C'est assez simple, puisqu'il n'a que 3 méthodes :
  • hasNext()- renvoie vrai ou faux, selon qu'il y a un élément suivant dans la liste, ou que nous avons déjà atteint le dernier.
  • next()- renvoie l'élément suivant dans la liste
  • remove()- supprime un élément de la liste
Comme vous pouvez le voir, l'itérateur est fait sur mesure pour nos besoins, et en même temps il n'y a rien de compliqué à ce sujet. Supposons que nous voulions vérifier s'il y a un élément suivant dans notre liste, et l'afficher s'il y en a :

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
}
Sortie : Cat{name='Thomas'} Cat{name='Behemoth'} Cat{name='Lionel Messi'} Cat{name='Fluffy'} Comme vous pouvez le voir, le a déjà implémenté une méthode ArrayListspéciale pour créer un itérateur : iterator(). De plus, notez que lorsque nous créons un itérateur, nous spécifions la classe d'objets avec laquelle il fonctionnera ( <Cat>). L'essentiel est qu'un itérateur gère facilement notre tâche d'origine. Par exemple, supprimez le chat nommé "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);
Sortie : [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] Vous avez peut-être remarqué que nous n'avons spécifié ni l'index ni le nom dans la remove()méthode de l'itérateur ! L'itérateur est plus intelligent qu'il n'y paraît : remove()supprime le dernier élément renvoyé par l'itérateur. Comme vous pouvez le voir, il a fait exactement ce que nous voulions qu'il fasse :) En principe, c'est tout ce que vous devez savoir sur la suppression d'éléments d'un fichier ArrayList. Eh bien, presque tout. Dans la prochaine leçon, nous regarderons à l'intérieur de cette classe et verrons ce qui s'y passe lors de divers appels de méthode :) Jusque-là !
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION