CodeGym /Java blog /Véletlen /Elem törlése egy ArrayList-ből
John Squirrels
Szint
San Francisco

Elem törlése egy ArrayList-ből

Megjelent a csoportban
Szia! Az utolsó órán megismerkedtünk az ArrayListosztállyal, és megtanultuk, hogyan végezzük el ezzel az osztállyal a leggyakoribb műveleteket. Ezenkívül rámutattunk néhány különbségre egy ArrayListés egy közönséges tömb között. De megkerültünk egy témát, nevezetesen, hogyan lehet elemeket törölni egy ArrayList. Ezt most megbeszéljük. Elem törlése egy ArrayList-ből - 1Már említettük, hogy az elemek törlése egy közönséges tömbből nem túl kényelmes. Mivel magát az elemet nem tudjuk törölni, ezért csak az értékét tudjuk "nullázni" (nullra állítani):

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 + '\'' +
               '}';
   }
}
Kimenet: [Cat{name='Thomas'}, null, Cat{name='Lionel Messi'}] De ha egy tömbelemet nullra állítunk, az "lyukat" hagy. Nem távolítottuk el a pozíciót a tömbben, csak a tartalmát. Képzeld el, mi történne, ha lenne egy 50 macskából álló tömbünk, és 17 macskát így távolítanánk el. Lesz egy tömbünk 17 lyukkal. Csak próbáld nyomon követni őket! Nem reális elvárni, hogy emlékezzen az üres cellák számára, ahová új értékeket írhat. Ha hibázik, felülírja a kívánt objektumhivatkozást. Természetesen van mód erre egy kicsit körültekintőbben: egy elem eltávolítása után mozgasd az elemeket a tömb elejére, hogy a végére kerüljön a "lyuk":

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));
}
Kimenet: [Cat{name='Thomas'}, Cat{name='Fluffy'}, Cat{name='Fluffy'}, null] Ez jobbnak tűnik, de aligha nevezhető robusztus megoldásnak. Ha másért nem, mint amiatt, hogy ezt a kódot minden alkalommal meg kell írnunk, amikor egy elemet törölünk egy tömbből! Ez egy rossz lehetőség. Eljárhatunk más úton is, és létrehozhatunk egy külön módszert:

public void deleteCat(Cat[] cats, int indexToDelete) {
   //...delete the cat corresponding to the index and move the elements
}
De ennek is kevés a haszna: ez a módszer csak objektumokkal működik Cat, más típusokkal nem. Más szóval, ha egy programnak további 100 osztálya van, amelyeket tömbökkel szeretnénk használni, akkor mindegyikbe ugyanazt a metódust kell pontosan ugyanazzal a logikával írnunk. Ez egy teljes katasztrófa -_- De az ArrayListosztály megoldja ezt a problémát! Speciális módszert valósít meg az elemek eltávolítására: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());
}
Az objektum indexét átadjuk a metódusnak, amely törli (akárcsak egy tömbben). A remove()módszernek két különlegessége van. Először is, nem hagy "lyukakat". Már megvalósítja azt a logikát, amely az elemek eltolásához szükséges, amikor egy elemet eltávolítunk a közepéről, amit korábban magunk írtunk. Nézd meg az előző kód kimenetét:

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

[Cat{name='Thomas'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
Egy macskát eltávolítottunk a közepéről, a többit pedig áthelyeztük, hogy ne legyen üres hely. Másodszor , nem csak index alapján törölheti az objektumokat (mint egy normál tömb), hanem hivatkozással is :

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());
}
Kimenet: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}] [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] Ez nagyon kényelmes lehet, ha nem szeretné mindig nyomon követni a kívánt objektum indexét. Úgy tűnik, rájöttünk a szokásos törlésre. Most képzeljük el ezt a helyzetet: szeretnénk ismételni a listánkat, és eltávolítani egy adott nevű macskát . Ehhez egy gyors hurkot (más néven for-each hurkot) használunk for, amellyel Rishi óráin ismerkedtünk meg:

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);
}
A kód teljesen logikusnak tűnik. De az eredmény nagy meglepetés lehet: Kivétel a "main" szálban java.util.ConcurrentModificationException itt: java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) itt: java.util.ArrayList$Itr.next(ArrayList. java:831) itt: Cat.main(Cat.java:25) Valamiféle hiba történt, és nem világos, hogy miért történt. Ez a folyamat számos árnyalattal jár, amelyekkel foglalkozni kell. Íme az általános szabály, amelyet észben kell tartania: Nem lehet egyszerre egy gyűjteményt iterálni és elemeit módosítani. És ez alatt mindenféle változtatást értünk, nem pusztán az eltávolítást. Ha a macska eltávolítását új macskák behelyezésére tett kísérlettel helyettesíti, az eredmény ugyanaz lesz:

for (Cat cat: cats) {

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

System.out.println(cats);
Kivétel a "main" szálban java.util.ConcurrentModificationException itt: java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) itt: java.util.ArrayList$Itr.next(ArrayList.java:831) itt: Cat.main( Cat.java:25) Az egyik műveletet egy másikra cseréltük, de az eredmény nem változott: ugyanazt a ConcurrentModificationException -t kapjuk . Pontosan akkor fordul elő, amikor megpróbáljuk megszegni a fenti szabályt úgy, hogy megváltoztatjuk a listát, miközben iterálunk rajta. Java-ban szükségünk van egy speciális objektumra, amelyet iterátornak (Iteratorosztálynak) neveznek, hogy töröljük az elemeket a gyűjteményben való iteráció során. AzIteratorosztály felelős az elemek listáján való biztonságos iterációért. Nagyon egyszerű, mivel csak 3 módszere van:
  • hasNext()- igaz vagy hamis értéket ad vissza, attól függően, hogy van-e következő elem a listában, vagy már elértük az utolsót.
  • next()- a lista következő elemét adja vissza
  • remove()- eltávolít egy elemet a listáról
Amint látja, az iterátor a mi igényeinkre szabott, ugyanakkor nincs benne semmi bonyolult. Tegyük fel, hogy ellenőrizni akarjuk, hogy van-e következő elem a listánkban, és megjelenítjük, ha van:

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
}
Kimenet: Cat{name='Thomas'} Cat{name='Behemoth'} Cat{name='Lionel Messi'} Cat{name='Fluffy'} Mint látható, a(z) már bevezetett egy ArrayListspeciális módszert egy iterátor: iterator(). Ezenkívül vegye figyelembe, hogy az iterátor létrehozásakor megadjuk az objektumok osztályát, amelyekkel az működni fog ( <Cat>). A lényeg az, hogy egy iterátor könnyedén kezeli az eredeti feladatunkat. Például távolítsa el a "Lionel Messi" nevű macskát:

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);
Kimenet: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] Lehet, hogy észrevette, hogy nem adtuk meg sem az indexet, sem a nevet az iterátor remove()metódusában ! Az iterátor okosabb, mint amilyennek látszik: remove()eltávolítja az iterátor által visszaadott utolsó elemet. Amint látja, pont azt csinálta, amit szerettünk volna :) Elvileg ez minden, amit tudni kell az elemek eltávolításáról egy ArrayList. Nos, szinte mindent. A következő leckében belenézünk ebbe az osztályba, és megnézzük, mi történik ott a különböző metódushívások során :) Addig is!
Hozzászólások
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION