Author
Volodymyr Portianko
Java Engineer at Playtika

Java ArrayList

Published in the Java Collections group
Hi! In previous lessons, we did a deep dive into arrays and reviewed common examples of working with arrays. In this lesson, we'll take a closer review at Java ArrayList. In general, arrays are super handy. And, as you've already noticed, you can do a lot with them :) But arrays do have a number of shortcomings.
  • Limited size. You need to know how many elements your array needs to contain at the time you create it. If you underestimate, then you won't have enough space. Overestimate, and the array will remain half-empty, which is also bad. After all, you're still allocating more memory than is necessary.

  • An array doesn't have methods for adding elements. You must always explicitly indicate the index of the position where you want to add an element. If you accidentally specify the index for a position occupied by some value you need, it will be overwritten.

  • There are no methods to delete an item. A value can only be "zeroed out".

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'}] Fortunately, Java's creators are well aware of arrays' advantages and disadvantages, and therefore created a very interesting data structure called Java ArrayList. Speaking as simply as possible, an Java ArrayList is a "souped up" array with a lot of new features.

How to Create an ArrayList

It is very easy to create:

ArrayList<Cat> cats = new ArrayList<Cat>();
Now we've created a list for storing Cat objects. Note that we aren't specifying the size of the ArrayList, because it can expand automatically. How is this possible? It's quite simple, actually. It may surprise you, but ArrayList in Java is built on top of a very ordinary array :) Yes, it contains an array, and that's where our elements are stored. But ArrayList in Java has a special way of working with that array:
  • When the internal array is filled, ArrayList creates a new array internally. The size of the new array is the size of the old array times 1.5 plus 1.

  • All the data is copied from the old array into the new one

  • The old array is cleaned up by the garbage collector.
This mechanism allows Java ArrayList (unlike an ordinary array) to implement a method for adding new elements. It's the add() method

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<Cat>();
   cats.add(new Cat("Behemoth"));
}
New items are added to the end of the list. Now there's no risk overflowing the array, so this method is completely safe. By the way, ArrayList can not only find an object by its index, but also vice versa: it can use a reference to find an object's index in the ArrayList! This is what the indexOf() method is for: We pass a reference to the object we want, and indexOf() returns its index:

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);

   int thomasIndex = cats.indexOf(thomas);
   System.out.println(thomasIndex);
}
Output: 0 That's right. Our thomas object is indeed stored in element 0. Arrays don't only have drawbacks. They also have unquestionable advantages. One of them is the ability to search elements by index. Because we point to an index, i.e. to a specific memory address, searching an array in this manner is very quick. ArrayList knows how to do it, too! The get() method implements this:

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);

   Cat secondCat = cats.get(1);

   System.out.println(secondCat);
}
Output: Cat{name='Behemoth'} In addition, you can easily find out whether the ArrayList contains a particular object.This is done using the ArrayListcontains() method:

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);

   cats.remove(fluffy);
   System.out.println(cats.contains(fluffy));
}
The method checks whether the ArrayList's internal array contains the element, and returns a boolean (true or false). Output: false And another important thing about insertion. ArrayList lets you use an index to insert elements not only at the end of the array, but anywhere. It has two methods for this:
  • ArrayListadd(int index, Cat element)
  • ArrayListset(int index, Cat element)
As arguments, both of these methods take the index of the position where you want to insert, and a reference to the object itself. The difference is that inserting using set() overwrites the old value. Inserting using add() first shifts by one all elements from [index] to the end of the array, and then adds the specified object in the resulting empty position.

Here's an example:


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);

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

   cats.set(0, lionel);// Now we have a list of 2 cats. Adding a 3rd using set

   System.out.println(cats.toString());
}
Output: [[Cat{name='Thomas'}, Cat{name='Behemoth'}] [Cat{name='Lionel Messi'}, Cat{name='Behemoth'}] We had a list of 2 cats. Then we inserted another one as element 0 using the set() method. As a result, the old element has been replaced by a new one.

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);

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

   cats.add(0, lionel);// Now we have a list of 2 cats. Adding a 3rd using add

   System.out.println(cats.toString());
}
And here we see that add() works differently. It moves all the elements to the right and then writes the new value as element 0. Output: [Cat{name='Thomas'}, Cat{name='Behemoth'}] [Cat{name='Lionel Messi'}, Cat{name='Thomas'}, Cat{name='Behemoth'}] To completely clear the list, we use the clear() method:

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);

   cats.clear();

   System.out.println(cats.toString());
}
Output: [] Everything was removed from the list. By the way, please note: unlike arrays, ArrayList overrides the toString() method and already displays the list appropriately as strings. With ordinary arrays, we had to use the Arrays class for this. And since I mentioned Arrays: Java lets you easily "switch" between an array and an ArrayList, i.e. convert one to another. The Arrays class has an Arrays.asList() method for this. We use it to get the contents as an array and pass them to our ArrayList constructor:

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");

   Cat[] catsArray = {thomas, behemoth, lionel, fluffy};

   ArrayList<Cat> catsList = new ArrayList<>(Arrays.asList(catsArray));
   System.out.println(catsList);
}
Output: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}] You can also go in the opposite direction: get an array from an ArrayList object. We do this using the toArray() method:

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);

   Cat[] catsArray = cats.toArray(new Cat[0]);

   System.out.println(Arrays.toString(catsArray));
}
Note: we passed an empty array to the toArray() method. This is not an error. Inside the ArrayList class, this method is implemented in such a way that passing an empty array increases its performance. Just keep this in mind for the future (of course, you can pass an array of some specific size; that will also work). Oh, about the size. The list's current size can be found using the size() method:

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.size());
}
It's important to understand that unlike an array's length property, the ArrayList.size() method returns the actual number of elements, not the original capacity. After all, we didn't specify a size when creating the ArrayList. However, you can specify it — ArrayList has a suitable constructor. But in terms of adding new elements, this doesn't change its behavior:

public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>(2);// create an ArrayList with an initial capacity of 2


   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.size());
}
Console output: 4 We've created a list of 2 elements, but it quietly expanded when we needed it to. Another consideration is that if we create a very small list initially, it will have to expand more often, which will use some resources. We hardly touched on the process of removing elements from an ArrayList in this lesson Of course, this isn't because it slipped our mind. We've put this topic into a separate lesson that you'll meet later :) To reinforce what you learned, we suggest you watch a video lesson from our Java Course
Comments (31)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Allpaka Level 14, Buenos Aires, Argentina
10 December 2023
vamos Leo Messi!
P.B.Kalyan Krishna Level 22, Guntur, India
26 June 2023
In the first program, the toString() is overridden in Cat class. Whereas we are calling the toString() of the Arrays class. The Arrays class had already overridden the toString() which it got from the Object class. The toString() of the Arrays class will be implemented and what is the use of overriding the toString() in the Cat class?
PeterC Level 28, France, France
31 October 2021
"Note: we passed an empty array to the toArray() method. This is not an error. Inside the ArrayList class, this method is implemented in such a way that passing an empty array increases its performance. Just keep this in mind for the future (of course, you can pass an array of some specific size; that will also work)." What was the point of that?
Max Sudik Level 41, Vancouver, Canada
31 March 2021
What is thee purpose of

ArrayList<Cat> cats = new ArrayList<>();
in

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);

   Cat[] catsArray = cats.toArray(new Cat[0]);

   System.out.println(Arrays.toString(catsArray));
}
Victoria Level 11, Cape Town, South Africa
16 July 2020
cats.add(0, lionel);// Now we have a list of 2 cats. Adding a 3rd using add This line also doesnt make sense because the add should mean 'now we have a list of 3 cats' and not just two. Or am I reading it wrong?
Victoria Level 11, Cape Town, South Africa
16 July 2020
cats.set(0, lionel);// Now we have a list of 2 cats. Adding a 3rd using set the commenting for the line above doesn't make sense because the 'add' function will add another element but the 'set' function overwrites the existing one meaning that you not realy adding a 3rd element. there is still only two.
Dyrits Level 22
15 July 2020
Why this article comes after the tasks about ArrayList ?
Hankster Level 9, Cleveland, US of A
15 February 2020
Above, it says that ArrayList overrides the toString() method, but right above that statement the code example shows: System.out.println(cats.toString()); where cats is an ArrayList. If cats is an ArrayList, then shouldn't we be able to simply write: System.out.println(cats); and get the correct printout, since the println() method will call the toString() method of the object inside?
Austeja Level 10, Kaunas, Lithuania
24 January 2020
Great lesson!
Derek Level 14, An Longfort, Ireland
30 December 2019
It states that ArrayList overrides the toString() method but i have found that this is maybe not not the case? public static void main(String[] args) { ArrayList<Dog> dogs = new ArrayList<>(); Dog thomas = new Dog("Thomas"); Dog behemoth = new Dog("Behemoth"); Dog lionel = new Dog("Lionel Messi"); Dog fluffy = new Dog ("Fluffy"); dogs.add(thomas); dogs.add(behemoth); dogs.add(lionel); dogs.add(fluffy); Dog secondDog = dogs.get(1); System.out.println(secondDog); System.out.println(dogs.contains(behemoth)); } @Override public String toString(){ return name; } I had to add in the @Override code as before I did it was printing what looked the memory location for behemoth? Any help would be greatly appreciated