JavaRush /Blog Java /Random-FR /Supprimer un élément d'une ArrayList en Java

Supprimer un élément d'une ArrayList en Java

Publié dans le groupe Random-FR
Bonjour! Dans la dernière leçon, nous nous sommes familiarisés avec la classe ArrayList et avons également appris à effectuer les opérations les plus courantes avec elle. De plus, nous avons mis en évidence quelques différences entre ArrayList et un tableau classique. Voyons maintenant comment supprimer un élément de ArrayList. Nous avons déjà dit que supprimer des éléments dans un tableau normal n'est pas très pratique. Supprimer un élément d'une ArrayList - 1Puisque nous ne pouvons pas supprimer la cellule elle-même, nous pouvons uniquement « remettre à 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("Hippopotamus");
       cats[2] = new Cat("Philip Markovich");

       cats[1] = null;

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


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

[Cat{name='Томас'}, null, Cat{name='Фorпп Маркович'}]
Mais une fois réinitialisé, un « trou » reste dans le tableau. Nous ne supprimons pas la cellule, mais seulement son contenu. Imaginez ce qui se passerait si nous avions un tableau de 50 chats, dont 17 seraient supprimés de cette manière. Nous aurons un réseau de 17 trous et nous en occuperons ! Se souvenir par cœur du nombre de cellules vides dans lesquelles vous pouvez écrire de nouvelles valeurs est irréaliste. Faites une erreur une fois et vous écraserez la cellule avec la référence souhaitée à l'objet. Il y a bien sûr la possibilité de le faire un peu plus prudemment : après suppression, déplacez les éléments du tableau au début, pour que le « trou » soit à la fin :
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));
}
Conclusion:

[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}, null]
Maintenant, cela semble mieux, mais cela peut difficilement être qualifié de solution stable. Au minimum, car nous devrons écrire ce code à la main à chaque fois que nous supprimerons un élément du tableau ! Mauvaise option. Vous pouvez procéder dans l'autre sens et créer une méthode distincte :
public void deleteCat(Cat[] cats, int indexToDelete) {
   //...remove the cat by index and shift the elements
}
Mais cela est également de peu d'utilité : cette méthode ne peut fonctionner qu'avec des objets Cat, mais ne peut pas fonctionner avec d'autres. Autrement dit, s'il y a 100 classes supplémentaires dans le programme avec lesquelles nous voulons utiliser des tableaux, nous devrons écrire la même méthode avec exactement la même logique dans chacune d'elles. C'est un échec complet -_- Mais dans la classe ArrayList, ce problème est résolu avec succès ! 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("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());
}
Nous avons transmis l'index de notre objet à la méthode, et il a été supprimé (comme dans un tableau). La méthode remove()présente deux caractéristiques. Premièrement , il ne laisse pas de « trous ». Il implémente déjà la logique de déplacement d'éléments lors de la suppression d'un élément du milieu, que nous avons précédemment écrite à la main. Regardez le résultat du code précédent dans la console :

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
Nous avons retiré un chat du milieu et les autres ont été déplacés afin qu'il n'y ait pas d'espace. Deuxièmement , il peut supprimer un objet non seulement par index (comme un tableau normal), mais également par référence à l'objet :
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());
}
Conclusion:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
Cela peut être très pratique si vous ne souhaitez pas toujours garder en tête l’index de l’objet souhaité. Il semble que nous ayons réglé la suppression habituelle. Imaginons maintenant cette situation : nous souhaitons parcourir notre liste d'éléments et supprimer un chat portant un certain nom. Pour cela, nous utilisons un opérateur de boucle spécial for- for each. Vous pouvez en apprendre davantage à ce sujet dans cette conférence .
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);
}
Le code semble assez logique. Cependant, le résultat pourrait vous surprendre :

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)
Une sorte d'erreur, et on ne sait pas pourquoi elle est soudainement apparue. Il y a un certain nombre de nuances dans ce processus qui doivent être prises en compte. Une règle générale dont vous devez vous souvenir : vous ne pouvez pas parcourir une collection et modifier ses éléments en même temps. Oui, oui, exactement un changement, et pas seulement une suppression. Si vous essayez dans notre code de remplacer la suppression des chats par l'insertion de nouveaux, le résultat sera le même :
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)
Nous avons changé une opération en une autre, mais le résultat n'a pas changé : la même erreur ConcurrentModificationException. Cela se produit précisément lorsque nous essayons d’enfreindre une règle et de modifier la liste tout en la parcourant. En Java, pour supprimer des éléments lors d'une itération, vous devez utiliser un objet spécial - un itérateur (classe Iterator). La classe Iteratorest responsable de parcourir en toute sécurité une liste d’éléments. C'est assez simple car il n'y a que 3 méthodes :
  • hasNext()- renvoie truesoit falseselon qu'il y a un élément suivant dans la liste, soit selon que l'on a déjà atteint le dernier.
  • next()- renvoie l'élément suivant de la liste
  • remove()- supprime un élément de la liste
Comme vous pouvez le voir, l'itérateur est littéralement « adapté » à nos besoins, et il n'a rien de compliqué. Par exemple, nous voulons vérifier si notre liste contient l'élément suivant, et si oui, l'imprimer sur la 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
}
Conclusion:

Cat{name='Томас'}
Cat{name='Бегемот'}
Cat{name='Фorпп Маркович'}
Cat{name='Пушок'}
Comme vous pouvez le voir, la classe ArrayListimplémente déjà une méthode spéciale pour créer un itérateur - iterator(). Notez également que lors de la création d'un itérateur, nous spécifions la classe d'objets avec laquelle il fonctionnera ( <Cat>). En fin de compte, nous pouvons facilement résoudre notre problème initial en utilisant un itérateur. Par exemple, supprimons un chat nommé « 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);
Conclusion:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
Vous avez peut-être remarqué que nous n'avons spécifié ni l'index de l'élément ni le nom de la variable de référence dans la méthode itérateur remove()! L'itérateur est plus intelligent qu'il n'y paraît : la méthode remove()supprime le dernier élément renvoyé par l'itérateur. Comme vous pouvez le voir, cela a fonctionné exactement comme nécessaire :) C'est essentiellement tout ce que vous devez savoir sur la suppression d'éléments de ArrayList. Plus précisément, presque tout. Dans la prochaine conférence, nous examinerons « l'intérieur » de cette classe et verrons ce qui s'y passe pendant les opérations :) À bientôt !
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION