Deleting an element from an ArrayList

Published in the Java Developer group
Hi! In the last lesson, we got acquainted with the ArrayList class, and learned how to perform the most common operations with this class. In addition, we pointed out several differences between an ArrayList and an ordinary array. But we skirted one topic, namely, how to delete elements from an ArrayList. We'll discuss that now. Deleting an element from an ArrayList - 1We've already mentioned that deleting elements from an ordinary array is not very convenient. Since we can't delete the element itself, we can only "zero out" (set to null) 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("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'}] But setting an array element to null leaves a "hole". We haven't removed the position in the array, only its contents. Imagine what would happen if we had an array of 50 cats and removed 17 of them this way. We'll have an array with 17 holes. Just try keeping track of them! It's unrealistic to expect to remember the number of empty cells where you can write new values. If you make one mistake, you'll overwrite an object reference that you want. There is, of course, a way to do this a little more carefully: after removing an element, move the elements to the front of the array to put the "hole" at the end:

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] This seems better, but it can hardly be called a robust solution. If for no other reason than the fact that we have to write this code every time when we delete an element from an array! This is a bad option. We could go another way and create a separate method:

public void deleteCat(Cat[] cats, int indexToDelete) {
   //...delete the cat corresponding to the index and move the elements
}
But this is also of little use: this method can only work with Cat objects, but not other types. In other words, if a program has another 100 classes that we want to use with arrays, we'll have to write the same method with exactly the same logic in each of them. This is a total disaster -_- But the ArrayList class solves this problem! 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("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());
}
We pass our object's index to the method, which deletes it (just like in an array). The remove() method has two special features. First, it does not leave "holes". It already implements the logic needed to shift elements when an element is removed from the middle, which we previously wrote ourselves. Look at the output from the previous code:

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

[Cat{name='Thomas'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
We removed one cat from the middle, and the rest were moved so that there were no empty spaces. Second, it can delete objects not only by index (like a normal array), but also by reference:

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'}] This can be very convenient if you don't want to always keep track of the desired object's index. It seems we've figured out ordinary deletion. Now let's imagine this situation: we want to iterate over our list and remove a cat with a specific name. To do this, we'll use a fast for loop (also called a for-each loop), which we were introduced to in Rishi's lessons:

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);
}
The code looks perfectly logical. But the result might be a big surprise: 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) There's some sort of an error, and it's unclear why it occurred. This process involves a number of nuances that must be addressed. Here's the general rule you need to remember: You cannot simultaneously iterate over a collection and change its elements. And we mean any sort of change, not merely removal. If you replace the cat removal with an attempt to insert new cats, the result will be the same:

for (Cat cat: cats) {

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

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 didn't change: we get the same ConcurrentModificationException. It occurs precisely when we try breaking the rule above by changing the list while iterating over it. In Java, we need a special object called an iterator (Iterator class) to delete items while iterating over a collection. The Iterator class is responsible for safely iterating over the list of elements. It is quite simple, since it has only 3 methods:
  • hasNext() - returns true or false, depending on whether there is a next item in the list, or we have already reached the last one.
  • next() - returns the next item in the list
  • remove() - removes an item from the list
As you can see, the iterator is tailor-made for our needs, and at the same time there's nothing complicated about it. Suppose we want to check if there is a next element in our list, and display it if there is:

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'} As you can see, the ArrayList has already implemented a special method for creating an iterator: iterator(). Additionally, note that when we create an iterator, we specify the class of objects that it will work with (<Cat>). The bottom line is that an iterator easily handles our original task. For example, remove the cat named "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'}] You may have noticed that we didn't specify either the index or the name in the iterator's remove() method! The iterator is smarter than it may appear: remove() removes the last element returned by the iterator. As you can see, it did just what we wanted it to do :) In principle, this is everything you need to know about removing elements from an ArrayList. Well, almost everything. In the next lesson, we'll look inside this class, and see what happens there during various method calls :) Until then!

More reading:

Comments (30)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Lisa Level 41
1 August 2021
Just to recap, the for-each loop throws a ConncurrentModificationException when an element of a collection is iterated and deleted at the same time. This is because the for-each loop uses an Iterator internally. To avoid the exception we would need to use this Iterator to remove the element, but it is not visible "outside" and therefore not usable by us users of this loop. Using a regular loop is still possible. You just need to manage the index yourself.

        for (int i = 0; i < cats.size();)
            if (cats.get(i).name.equals("Behemoth"))
                cats.remove(cats.get(i));
            else
                i++;
Andy Lee Level 10
2 March 2021
使用迭代器以在迭代过程操作集合元素
ъуъ Level 9, Vilnius, Lithuania
8 October 2020
That was more than enough cats for today...
Mariam Level 9, Columbus, United States
12 September 2020
I still do not know how to: 1) copy fifteen element from the bottom of the ArrayList to the top of the ArrayList 2) delete those bottom ones
Aakankasha Sharma Level 18, Chandigarh, India
10 June 2020
We could go another way and create a separate method:


public void deleteCat(Cat[] cats, int indexToDelete) {
   //...delete the cat corresponding to the index and move the elements
}
But this is also of little use: this method can only work with Cat objects, but not other types. But can't we create a function that accepts an array of Objects, more like Object[] object, int indexToDelete? What are the possible issues that may arise (one is that I'll have to create this function everytime) ? Also curiously, what implementation does the ArrayList use when remove(int index) function is called?
Kent Hervey Level 16, United States
21 November 2019
I love the humor in the name of this cat...from a funny TV show: cats.add(new Cat("Salem Saberhagen"));
Skynet Level 40, USA
19 November 2019
Instead of the old Iterator constructor, you can now use a one line lambda expression to remove an element, such as the one in the example above:

cats.removeIf(nextCat -> nextCat.name.equals("Lionel Messi"));
Fadi Alsaidi Level 31, Carrollton, TX, USA
7 November 2019
Not sure if anyone wondered about why is it that we didn't use the word "new" to instantiate a new Iterator object? At first, I thought maybe all the Iterator methods are static and require no instantiation using the word "new". WRONG. Come to find out that when calling out the Arraylist method "Iterator", the method it self has the code to create new instance of Iterator object.
ashgolan Level 8, Ashdod, Israel
31 October 2019
very important :)
P.B.Kalyan Krishna Level 18, Guntur, India
19 September 2019
Seems to be a very good lesson. Now apply the lesson in practice.