JavaRush /Java Blog /Random EN /Removing an element from an ArrayList in Java

Removing an element from an ArrayList in Java

Published in the Random EN group
Hello! In the last lecture, we got acquainted with the ArrayList class , and also learned how to perform the most common operations with it. In addition, we have identified quite a few differences between ArrayList and a regular array. Now let's look at removing an element from the ArrayList. We have already said that deleting elements in a regular array is not very convenient. Removing an element from an ArrayList - 1Since we cannot delete the cell itself, we can only “zero” its value:

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пп Маркович'}]
But when reset, a “hole” remains in the array. We are not deleting the cell, but only its contents. Imagine what will happen if we have an array of 50 cats, 17 of which we deleted in this way. We'll have an array with 17 holes, and look after them! Remembering by heart the numbers of empty cells where you can write new values ​​is unrealistic. Make a mistake once and you will overwrite the cell with the desired reference to the object. There is, of course, an opportunity to do it a little more carefully: after deleting, move the elements of the array to the beginning, so that the “hole” is at the end:

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]
Now it seems to look better, but this can hardly be called a stable solution. At a minimum, because we will have to write this code by hand every time we remove an element from the array! Bad option. You could go the other way and create a separate method:

public void deleteCat(Cat[] cats, int indexToDelete) {
   //...remove the cat by index and shift the elements
}
But this is also of little use: this method can only work with objects Cat, but cannot work with others. That is, if there are 100 more classes in the program with which we want to use arrays, we will have to write the same method with exactly the same logic in each of them. This is a complete failure -_- But in the ArrayList class this problem is successfully solved! It implements a special method for removing elements - 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());
}
We passed the index of our object to the method, and it was deleted (just like in an array). The method remove()has two features. Firstly , it does not leave “holes”. It already implements the logic of shifting elements when removing an element from the middle, which we previously wrote by hand. Look at the output of the previous code in the console:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
We removed one cat from the middle and the others were moved around so that there were no gaps. Secondly , it can delete an object not only by index (like a regular array), but also by reference to the object :

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='Пушок'}]
This can be very convenient if you don’t want to always keep the index of the desired object in your head. We seem to have sorted out the usual deletion. Now let's imagine this situation: we want to iterate through our list of elements and remove a cat with a certain name. For this we use a special loop operator for- for each. You can learn more about it in this lecture .

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);
}
The code seems to look quite logical. However, the result may surprise you:

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)
Some kind of error, and it is unclear why it suddenly appeared. There are a number of nuances in this process that need to be dealt with. A general rule that you need to remember: You cannot iterate through a collection and change its elements at the same time. Yes, yes, exactly a change, and not just a deletion. If you try in our code to replace the removal of cats with the insertion of new ones, the result will be the same:

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)
We changed one operation to another, but the result did not change: the same error ConcurrentModificationException. It occurs precisely when we try to break a rule and change the list while iterating through it. In Java, to remove elements during iteration, you need to use a special object - an iterator (class Iterator). The class Iteratoris responsible for safely walking through a list of elements. It is quite simple because it has only 3 methods:
  • hasNext()- returns trueeither falsedepending on whether there is a next element in the list, or whether we have already reached the last one.
  • next()- returns the next element of the list
  • remove()- removes an element from the list
As you can see, the iterator is literally “tailored” to our needs, and there is nothing complicated about it. For example, we want to check if our list contains the following element, and if so, print it to the 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='Пушок'}
As you can see, the class ArrayListalready implements a special method for creating an iterator - iterator(). Also, note that when creating an iterator, we specify the class of objects with which it will work ( <Cat>). Ultimately, we can easily solve our original problem using an iterator. For example, let’s delete a cat named “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='Пушок'}]
You may have noticed that we did not specify either the element index or the reference variable name in the iterator method remove()! The iterator is smarter than it may seem: the method remove()removes the last element that was returned by the iterator. As you can see, it worked exactly as needed :) That's basically everything you need to know about removing elements from ArrayList. More precisely - almost everything. In the next lecture we will look into the “insides” of this class and see what happens there during operations :) See you!
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION