The java.util.List interface is a subtype of Collection. It maintains the insertion order of elements and allows for positional access. Unlike Sets, Lists typically allow duplicate elements and multiple null values, making them the go-to choice for stacks, queues, and general data storage.
Before choosing a specific implementation like ArrayList or LinkedList, you must understand the rules that all Lists follow:
The List interface has four primary implementations, each with a different internal "engine":
| Implementation | Internal Structure | Best For... |
|---|---|---|
| ArrayList | Dynamic Array | Frequent reading and random access. |
| LinkedList | Doubly Linked List | Frequent insertions or deletions at the start/middle. |
| Vector | Synchronized Array | Legacy apps requiring thread-safety (Rarely used now). |
| Stack | LIFO Array | Last-In-First-Out operations (Legacy class). |
Beyond the standard add() and remove() found in all collections, the List interface provides positional methods:
void add(int index, E element): Inserts an element at a specific spot.E get(int index): Retrieves the element at that position.E set(int index, E element): Replaces the element at that position.int indexOf(Object o): Returns the index of the first occurrence of an item.List<E> subList(int fromIndex, int toIndex): Returns a view of a portion of the list.This is where performance matters. In an ArrayList, get(500) is instant ($O(1)$) because it calculates the memory address. In a LinkedList, get(500) is slow ($O(n)$) because Java must start at the beginning and follow 500 "next" pointers to find the item.
This snippet demonstrates how to manipulate a list using both traditional methods and modern Java 8+ functional approaches.
Standard iterators only go forward. However, the List interface provides a ListIterator which allows you to move backwards and forwards, and even modify the list during iteration.
ListIterator<String> lit = list.listIterator();while(lit.hasNext()) { ... } // Go Forwardwhile(lit.hasPrevious()) { ... } // Go Backward
In modern Java, Vector and Stack are considered "legacy." They are synchronized, meaning they have a lot of performance overhead for thread-safety that most single-threaded apps don't need.
Collections.synchronizedList() or CopyOnWriteArrayList.ArrayDeque.Q: What is the difference between Array and List?
A: Arrays have a fixed size and can hold primitives. Lists (like ArrayList) have a dynamic size and can only hold Objects (primitives are auto-boxed).
Q: How do you convert a List to an Array?
A: Use the toArray() method. Example: String[] arr = list.toArray(new String[0]);
Q: How does ArrayList grow?
A: When the internal array is full, ArrayList creates a new, larger array (usually 1.5x the original size) and copies all elements to the new one. This is why it's better to provide an initial capacity if you know you have a lot of data.
The List Interface is the backbone of data manipulation in Java. By understanding the trade-offs between ArrayList (speed of access) and LinkedList (speed of modification), you can write code that remains performant even as your data grows from hundreds to millions of records. Always program to the interface (List<String> list = new ArrayList<>()) to keep your code flexible.