I am starting a new thread to compare different ways to code the same functionality. In this post, I'll compare two common methods to create a simple list with only one element. Specifically, I'll examine the most commonly used List implementation constructor and the Collections.singletonList, a straightforward Factory Method for creating an immutable list containing a single element.
Array List
Everytime you initialize an ArrayList without specifying its initial capacity, it starts with an empty array. When you add the first element, the ArrayList resizes using a relatively havy algorithm that involves copying the array. Let's take a look at the ArrayList structure:
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
private Object[] grow() {
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
Here's what happens step-by-step:
- An initial empty array
elementDatais created. - When you add the first element, the current size (which is zero) is compared to the length of the array.
- Since it found out that the its size is zero, the array needs to grow to accomodate the new element.
- The
growmethod is called, which calculates the new capacity. - The
newCapacityis initially increased by half the old capacity, which let us with0 + 0/2 = 0. - Since
newCapacityis not sufficient to accomodate the element, it continues. - As it found out that
elementDatais the same initial empty array, it finally returns maximum between the required size (1) and theDEFAULT_CAPACTIY(10), resulting in an array of size 10.
This resizing process is quite complex when you only need a simple list that will always contain a single element.
That said, let's talk about our alternative!
Collection::singletonList
Method signature:
public static <T> List<T> singletonList(T o)
Description
This method returns an immutable list containing only the specified object. Introducted in Java 1.3, singletonList has several advantages:
- Inline implementation: You can initialize it with the desired element in a single line.
-
Immutability: Let's take a look at its implementation:
private static class SingletonList<E> extends AbstractList<E> implements RandomAccess, Serializable { private final E element; SingletonList(E obj) { element = obj; } ... }The
AbstractList, from whichSingletonListinherits, defines all mutable methods like this:
public boolean add(E e) { add(size(), e); return true; } public void add(int index, E element) { throw new UnsupportedOperationException(); } public E remove(int index) { throw new UnsupportedOperationException(); } public E set(int index, E element) { throw new UnsupportedOperationException(); }This ensures that it's impossible to chante the list's size or the content of its single element.
Immutability is a highly advantageous feature. While I won't delve into it here, interested developers can learn more from this article.
Memory Allocation: The
SingletonListclass contains only one simple field to accommodate the single element, unlikeArrayList, which uses an array that, using simpleArrayListsimple constructor, will leave us with an array with size of 10 after element addition.CPU usage: The
SingletonListconstructor accepts the single element as a parameter, requiring no resizing, array copy or maniputalion. This is far more efficient compared to theArrayListaddmethod.
Conclusion
In this post, we compared two ways to create a simple list with a single element: using the ArrayListconstructor and the Collection.singletonList method. While ArrayList is a flexible and commonly used data structure, it comes with some unnecessary overhead, particularly in terms of memory allocation and CPU usage when adding elements. This overhead includes resizing and copying arrays, which can be redundant for a list that is meant to hold only one element. However, if you need to change this element, ArrayList is a suitable solution.
On the other hand, Collection.singletonList offers a more efficient alternative for creating a single-element list. This method is not only more concise and easier to use but also ensures immutability, which can be a significant advantage in many scenarios. It has a minimal memory footprint and requires almost no CPU resources compared to ArrayList.
In summary, for a simple, immutable list containing only one element, Collection.singletonList is a superior choice due to its efficiency, simplicity, and immutability. However, if you need to modify the element in the list, the ArrayList could be an more appropriate choice.
On the next post I'll compare another alternative for a single-element list: List.of factory method. See you later!
Top comments (0)