Core Java

How List works internally in Java

List is one of common collections in Java. Here we are going to discuss about list and how it works internally in java.

1. List/ArrayList/LinkedList

A List is an ordered Collection. Lists may contain duplicate elements. In addition to the operations inherited from Collection, the list interface includes operations for the following:
 
 

  • Positional access (random access): manipulates elements based on their numerical position in the list. This includes methods such as get, set, add, addAll, and remove.
  • Search: searches for a specified object in the list and returns its numerical position. Search methods include indexOf and lastIndexOf.
  • Iteration: extends Iterator semantics to take advantage of the list’s sequential nature. The listIterator methods provide this behavior.
  • Range-view: The sublist method performs arbitrary range operations on the list.

There are two List implementations. ArrayList, which is usually the better-performing implementation, and LinkedList which offers better performance under certain circumstances.

In the example below, an object of ArrayList has been created. The add() method is invoked to add the element to the list. Then, the result are displayed. The question is how add() method works internally to add the elements to the list?

public static void main(String[] args) throws IOException {
        List<> list = new ArrayList<>();        
        list.add(20);        
        list.add("Java Code Geeks");
        list.add("ArrayList implementation in java");
        System.out.println(list);
}

Output:

[20, Java Code Geeks, ArrayList implementation in java]

There are two overloaded add() methods in ArrayList class:

  1. add(Object): adds the element to the end of the list.
  2. add(int index, Object): adds the element at the specified position in the list.

Both methods have a similar implementation. So, we are going to look at add(Object) method here.

2. ArrayList implementation inside Java

In the ArrayList class in java, the following array is defined to store the elements of the ArrayList.

private transient Object[] elementData;

There are two different ways to create an ArrayList object.

2.1 Create an empty list with initial capacity

When an object of ArrayList is created without initial capacity, the default constructor of the ArrayList class is invoked. It uses empty array instance to create the new object.

List<> list = new ArrayList<>();

the following code is executed:

private static final Object[] EMPTY_ELEMENTDATA = {}; // empty array instance
public ArrayList() {
    super();
    this.elementData = EMPTY_ELEMENTDATA; 
}

When an object of ArrayList is created with an initial capacity, the ArrayList constructor is invoked to create the array internally.

List<> list = new ArrayList<>(20);

and the following code is executed:

public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

The size of the array will be equal to the argument passed in the constructor. Then, size of the array will be 20 in above example

2.2. Create a non empty list containing the elements of a specified collection.

An object of ArrayList also can be created based on a specific collection.

List list = new ArrayList<>(Collection c);

then, the following code is executed:

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
}

The above ArrayList constructor will create an non empty list containing the elements of the collection passed in the constructor.

2.3. How the size of ArrayList grows dynamically?

In the add(Object), the capacity of the ArrayList will be checked before adding a new element. Here is the implementation of the add() method.

public boolean add(E e) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = e;
        return true;
}

ensureCapacityInternal() determines what is the current size of occupied elements and what is the maximum size of the array. If the size of the current elements (including the new element to be added to the ArrayList) is greater than the maximum size of the array then increase the size of array. But the size of the array can not be increased dynamically. So, what happens internally is, a new Array is created and the old array is copied into the new array. The new capacity of the array will be calculated as below:

        
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)   
            newCapacity = minCapacity;

        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);

In the above code, minCapacity is the size of the current elements (including the new element to be added to the ArrayList).

Tip
ArrayList uses shallow copy to copy the reference of the object to the new ArrayList instance.

When an ArrayList instance with no initial capacity is created and is empty, then, the add() method is invoked to add an element to the ArrayList instance, the following code is executed to apply a default size to the array.

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 
}

In the above code, minCapacity is the size of the current elements (including the new element to be added to the ArrayList). DEFAULT_CAPACITY is 10 in ArrayList class and DEFAULTCAPACITY_EMPTY_ELEMENTDATA is an empty array object.

3. Runtime performance of ArrayList

The size, isEmpty, get, set, iterator, and listIterator operations run in constant time O(1). The add operation runs in amortized constant time, that is, adding n elements requires O(n) time. All of the other operations run in linear time. The constant factor is low compared to that for the LinkedList implementation.

4. Conclusion

Now that you know how List works internally in Java, you might want to know about the implementation of Set or Map inside Java and how they work. Because these sort of questions shows that candidate has a good knowledge of Collection. You can checkout these examples:

Ima Miri

Ima is a Senior Software Developer in enterprise application design and development. She is experienced in high traffic websites for e-commerce, media and financial services. She is interested in new technologies and innovation area along with technical writing. Her main focus is on web architecture, web technologies, java/j2ee, Open source and mobile development for android.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button