Java ArrayList Tutorial with Examples

Feb 21, 2020 · 16 mins read

ArrayList Overview

If there’s one data structure that I get excited about and use a lot, it’s the ArrayList in Java.

ArrayLists are so very versatile and powerful and can be used in so many use cases.

I’ll make a bold claim. If you’re not sure, start out with ArrayList. The chance of you getting it wrong is small.

In this article, we’ll explore ArrayList and look at several examples.

The ArrayList is a class that is part of the Java Collections Framework.

A collection represents a group of objects. Such as Set, List, Queue, etc. The Java Collections Framework provides several classes that make it easy for developers to work with and use collections.

ArrayLists vs Arrays

ArrayLists are used for storing an ordered collection of objects. Any object stored in the ArrayList can be randomly access and retrieved in constant time.

Sounds a lot like Arrays? It sure does. However, there is a key difference. (At high-level, otherwise you can argue that there are several such as one being a class, whereas the other has built-in language support. But we’ll ignore these differences as they are not relevant.)

ArrayLists are dynamically sized which means that developers don’t need to specify the capacity or the maximum size when declaring ArrayLists. As elements are added and removed, it grows or shrinks its size automatically.

Contrast this to standard arrays in Java e.g. int []. These are fixed size, always occupying a fixed amount of memory. You must assign them a capacity during initialization. When standard arrays become full, they cannot be adjusted to make room for more elements. You must know in advance how many elements the array will hold and initialize accordingly.

Internals of ArrayLists

We just looked at the key difference between ArrayLists and Arrays but there is a twist.

Under the hood, ArrayLists class uses plain old arrays to store elements!

ArrayList implements the List interface. Internally, ArrayList class uses plain old arrays to store the elements, hence the name ArrayList.

When you add a new element, the ArrayList checks if there’s enough room in the internal array to store the new arrival. If there is, the element is added to the array. Otherwise, it grows the capacity of the internal array by allocating a larger array and moving existing elements to it. Likewise, when elements are removed and extra space isn’t needed, it shrinks the internal array. Note: The actual details of the growth policy is JDK dependent and the specification doesn’t specify when to grow (e.g. when the array is more than half full) or shrink the internal arrays.

Since we compared ArrayLists to Arrays, let’s talk about their performance and compare that as well.

ArrayLists may appear to be slower than Arrays as they do extra work of growing and shrinking its internal array which it uses to store the objects. Creating a new array and copying existing elements to it may take a long time! So does this mean Arrays are faster than ArrayLists?

The answer is no and yes. A neat computer science concept called amortized time tells us that while an operation may take a long time once in a while, it doesn’t happen most of the time. In other words, ArrayLists grow once in a while so the average insert time is a constant O(1), just like Arrays. Once in a while when the Array needs to increase in size, the insert time will be O(N), but it should only happen rarely.

So in essence, you can assume that both Arrays and ArrayLists have similar performance.

ArrayLists are similar to how dynamic arrays work in other programming languages such as Python.

Let’s summarize what we have learned so far:

  1. ArrayList Overview
    1. ArrayLists vs Arrays
    2. Internals of ArrayLists
  2. ArrayList Operations
  3. Examples
    1. Creating ArrayList & Adding Elements to it
    2. Removing Elements from ArrayList
    3. Iterating over ArrayList Elements
    4. Searching for Elements in ArrayList
  4. Closing Thoughts
    1. Multithreading
    2. Null and Duplicate Values
    3. ArrayList Sorting
    4. Initial Capacity
    5. ArrayList vs LinkedList
    6. CopyOnWriteArrayList - Thread safe Version
  5. Cheat Sheet

ArrayList Operations

Here are few notable operations of the ArrayList class which we’ll exlore in this tutorial.

  • add(element) to add a new element to the end of this list or add(index, element) to add element at the specified index.
  • addAll(collection) to append another collection (e.g. another ArrayList) to it.
  • set(index, element) replaces the element at the specified position in this list with the specified element.
  • get(index) to get an element at the index.
  • isEmpty() to check if there are any elements in this list. Returns true if this list contains no elements.
  • size() returns the number of elements in this list.
  • clear() to delete all elements. You can also use removeAll() to achieve the same, but it is slower than clear().
  • remove(index) removes the element at the index in this list. Shifts any subsequent elements to the left (subtracts one from their indices).
  • remove(object) removes the first occurrence of the specified element from this list.
  • removeIf(predicate) removes element from the ArrayList that satisfy the predicate argument e.g. removeIf(s -> s.length == 3)
  • contains(object), indexOf(object) and lastIndexOf(object) methods are used for searching for elements in ArrayList.
  • listIterator() to get an ordered list iterator over the elements in this list.
  • toArray() converts this ArrayList into an array.

Examples

Creating ArrayList & Adding Elements to it

In the first example, we’ll see how to:

  • create an ArrayList object and treat it like a standard array to illustrate the similarities. Please note that we don’t specify a size or capacity when creating the ArrayList. By default, initial capacity is set to 10, if you don’t specify it.
  • add elements to ArrayList using the add(index, element) method.
  • get elements from ArrayList using the get(index) method.
  • set elements at a specific index using the set(index) method.
import java.util.ArrayList;

public class ArrayListExample {

    public static void main(String[] args) {

        // Creating an array size with no initial capacity
        ArrayList<Integer> arrayList = new ArrayList<>();

        // Insert 10 elements into the list
        // Each element is loop index i + 100 eg 100 thru 109
        for (int i=0; i<10; i++) {
            arrayList.add(i, i+100);
        }

        // Print elements using for loop like standard arrays
        for (int i=0; i<10; i++) {
            System.out.println(arrayList.get(i));
        }

        // Let's change the item at 2nd position
        arrayList.set(1, 333);

        System.out.println("after change");
        System.out.println("Item at 2nd position i.e. arrayList[1]: " 
                + arrayList.get(1));
    }
}
Output
100
101
102
103
104
105
106
107
108
109
after change
Item at 2nd position i.e. arrayList[1]: 333


Removing Elements from ArrayList

Deleting elements from regular arrays at specific positions isn’t straighforward and requires a lot of work. When you delete an element, you’d either leave a hole (null or 0 etc.) at the position or have to shift subsequent elements to the left manually. ArrayList.remove(int index) not only removes the element at the specified index but also shifts all subsequent elements to the left. Let’s take a look at an example.

import java.util.ArrayList;

public class ArrayListExample2 {
    public static void main(String[] args) {
        // Creating an array list and insert some elements
        ArrayList<String> numbers = new ArrayList<>();
        numbers.add("ArrayList");
        numbers.add("Java");
        numbers.add("Arrays");
        numbers.add("List");
        numbers.add("Collections");

        System.out.println("original => "+ numbers);

        // Remove element at index 1
        numbers.remove(1);
        System.out.println("numbers.remove(1) => " + numbers);

        // Remove by object
        boolean removed = numbers.remove("Collections");
        System.out.println("numbers.remove(\"Collections\") => " + numbers);

        // Remove by predicate (Lambda)
        numbers.removeIf(s -> s.length() == 4);
        System.out.println("removeIf(s -> s.length() == 4) => " + numbers);

        //Delete all
        numbers.clear();
        System.out.println("numbers.clear() => " + numbers);
}
Output
original => [ArrayList, Java, Arrays, List, Collections]
numbers.remove(1) => [ArrayList, Arrays, List, Collections]
numbers.remove("Collections") => [ArrayList, Arrays, List]
removeIf(s -> s.length() == 4) => [ArrayList, Arrays]
numbers.clear() => []


Iterating over ArrayList Elements

In the next example, we’ll use different strategies of iterating over an ArrayList. These methods are not specific to ArrayList class and are available for all classes that extend the List interface.

  1. Using the standard for loop
  2. Using for-each construct
  3. Using ArrayList.forEach(…) and Consumer interface
  4. Using ArrayList.forEach(...) and Lambda expression
  5. Using ArrayList.forEach(...) and method reference

The example below shows all 5 ways of iterating over ArrayList listed above.

import java.util.ArrayList;
import java.util.function.Consumer;

public class ArrayListExample3 {

    public static void main(String[] args) {

        // Creating an array list
        ArrayList<Integer> numbers = new ArrayList<>();

        // Insert some elements
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);

        // Iterate using standard for loop
        for (int i=0; i<numbers.size(); i++) {
            System.out.println(i);
        }
        
        // Iterate using an iterator
        for (Integer num: numbers) {
            System.out.println(num);
        }
        
        // Iterate using ArrayList.forEach
        numbers.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer num) {
                System.out.println(num);
            }
        });
        
        // Iterate using forEach and Lambda
        numbers.forEach(num -> System.out.println(num));

        // Iterate using forEach and method reference
        numbers.forEach(System.out::println);
    }
}

Searching for Elements in ArrayList

We can search for elements in ArrayList using contains(object), indexOf(object) and lastIndexOf(object) methods.

import java.util.ArrayList;
public class ArrayList5 {
    public static void main(String[] args) {
        // Creating an array list and insert some elements
        ArrayList<String> numbers = new ArrayList<>();
        numbers.add("ArrayList");
        numbers.add("Java");
        numbers.add("Arrays");
        numbers.add("List");
        numbers.add("Collections");

        boolean result;

        result = numbers.contains("Collections");
        System.out.println("numbers.contains(\"Collections\") => " +  result);

        result = numbers.contains("C++"); // doesn't exist
        System.out.println("numbers.contains(\"C++\") => " +  result);

        int index;

        // Find the index of the first occurrence of an element
        index = numbers.indexOf("List");
        System.out.println("numbers.indexOf(\"List\") => " +  index);

        index = numbers.indexOf("Oracle"); // doesn't exist
        System.out.println("numbers.indexOf(\"Oracle\") => " +  index);

        // Find the index of the last occurrence of an element
        numbers.add("List"); // Add another "List" to the ArrayList
        index = numbers.lastIndexOf("List");
        System.out.println("numbers.indexOf(\"List\") => " +  index);
    }
}
Output
numbers.contains("Collections") => true
numbers.contains("C++") => false
numbers.indexOf("List") => 3
numbers.indexOf("Oracle") => -1
numbers.indexOf("List") => 5


Closing Thoughts

Multithreading

ArrayList class is not synchronized and hence it is not safe for multithreading. Access to it by multiple threads must be synchronized. You can also use the Collections.synchronizedList method which returns a synchronized (thread-safe) list backed by the specified list.

Null and Duplicate Values

ArrayList allows null and duplicate values. The following are valid operations.

arrayList.add(null); // null is allowed
arrayList.add(1);
arrayList.add(null); // insert another null; duplicates are allowed too!

ArrayList Sorting

You can sort elements in an ArrayList using the Collections.sort() method or the ArrayList.sort() method.

Initial Capacity

When creating ArrayList, you can specify an initial capacity. In earlier Java versions, it was quite small: 10. This requires a lot of resizing as elements were added. If you know how many elements you’d be storing in the ArrayList, it is best to construct it with that value as its initial capacity.

ArrayList vs LinkedList

Both ArrayList and LinkedList implement the List interface.

At the start of this post, I said ArrayList is so versatile that it can be used almost anywhere. It turns out I exaggerated a little bit about my favorite data structure.

There is one use case where LinkedList will perform better.

Use LinkedList over ArrayList if you need to insert or remove elements anywhere in the collection. If you need to do this a lot, the ArrayList class will need to shift all elements one space over to make room. This takes time. Because LinkedList uses pointers to point to the next element (and the previous as well, since LinkedList in Java is doubly linked list implementation), inserting an element in the middle takes constant time O(1) because all it requires is changing the pointers.

That being said, in reality, 90% of the time where I have seen LinkedLists being used instead of ArrayLists, it was for the wrong reasons. Everytime I encounter LinkedList, I challenge the author to explain what benefit they’d get out of using it over ArrayList and the answer is almost always negative.

CopyOnWriteArrayList - Thread safe Version

ArrayList is not thread-safe.

This means you cannot share an ArrayList between threads or use in concurrent code. At least not without external synchronization.

CopyOnWriteArrayList is an enhanced version of ArrayList that allows it to be used in a thread-safe manner without requiring external synchronization. When it is modified, it creates a new array and the internal array it uses to store the elements is copied into the new array.

Only use CopyOnWriteArrayList if traversal operations happens way more frequently than the modification operations. Otherwise, it’s a poor choice due to performance reasons and you’re better off using external synchronization mechanisms such as the synchronized blocks or by using Collections.synchronizedList as we discussed previously.

Cheat Sheet

Here’s a cheat sheet I created highlighting important methods of the ArrayList class.

arraylist-cheat-sheet

What is your favorite data structure in Java? Got any tips you want to share with the community? Leave a comment below.

#java #arraylist #list

You May Also Enjoy


If you like this post, please share using the buttons above. It will help CodeAhoy grow and add new content. Thank you!


Comments (2)


khoogland

Insightful article. Might I suggest alternate titles? “F Array List”or “just Use Array List”.


the architect

I’m a software architect and I concur with the Arraylist and Linkedlist comparison. It is jarring to see linked lists in practice and always useless. Only a small handful of cases where it is better.


Speak Your Mind