List Interface
java list interface ArrayList LinkedList Vector Stack ListIterator fail-fast ConcurrentModificationException Deque
Introduction
In the previous lesson, you learned about the Java Collections Framework (JCF) and its architecture. You saw that the Collection interface is divided into three major interfaces:
- List
- Set
- Queue
Among these, the List Interface is the most widely used collection in Java enterprise applications.
Almost every Spring Boot application uses Lists to:
- Retrieve database records
- Process REST API requests
- Store employee details
- Manage shopping carts
- Handle orders
- Work with JSON Arrays
- Process Kafka messages
Understanding the List Interface is essential for Java developers because it is one of the most frequently asked interview topics.
In this lesson, you'll learn:
- What is List?
- Characteristics of List
- ArrayList
- LinkedList
- Vector
- Stack
- Internal Architecture
- Memory Representation
- Time Complexity
- ArrayList vs LinkedList
- Vector vs ArrayList
- Stack Operations
- Fail-Fast vs Fail-Safe Iterators
- Best Practices
- Common Mistakes
- Real-world Examples
- Professional Interview Questions
What is the List Interface?
The List Interface represents an ordered collection of elements.
It allows:
- Duplicate elements
- Null values
- Index-based access
- Ordered iteration
Example:
List<String> fruits = new ArrayList<>();fruits.add("Apple");fruits.add("Banana");fruits.add("Apple");
Output:
AppleBananaApple
Notice that duplicates are allowed.
Characteristics of List
A List:
- Maintains insertion order
- Allows duplicate elements
- Supports indexing
- Allows null values
- Supports iteration
Example:
List<Integer> numbers = new ArrayList<>();numbers.add(10);numbers.add(20);numbers.add(30);System.out.println(numbers.get(1));
Output:
20
List Hierarchy
Iterable│Collection│List┌──┼──────────┐│ │ │ArrayList LinkedList│Vector│Stack
Common List Methods
add()add(index, element)get()set()remove()contains()indexOf()lastIndexOf()size()isEmpty()clear()
Example:
List<String> names = new ArrayList<>();names.add("Rahul");names.add("Amit");System.out.println(names.size());
Output:
2
ArrayList
ArrayList is the most commonly used implementation of the List interface.
Internally, it uses a dynamic array.
Example:
List<String> cities =new ArrayList<>();cities.add("Delhi");cities.add("Mumbai");cities.add("Hyderabad");
Internal Working of ArrayList
Memory:
+---------+---------+---------+---------+| Delhi | Mumbai | Hyderabad | null |+---------+---------+---------+---------+
When the internal array becomes full, Java automatically allocates a larger array and copies the existing elements into it.
This resizing is transparent to the developer.
ArrayList Time Complexity
| Operation | Complexity |
|---|---|
| get(index) | O(1) |
| set(index) | O(1) |
| add(end) | O(1) (Amortized) |
| insert(middle) | O(n) |
| remove(middle) | O(n) |
| contains() | O(n) |
ArrayList is excellent for read-heavy applications.
LinkedList
LinkedList is implemented as a doubly linked list.
Each node stores:
- Data
- Previous Reference
- Next Reference
Example:
List<String> employees =new LinkedList<>();employees.add("John");employees.add("David");
LinkedList Memory Representation
+------+ +------+ +------+|John |<--->|David |<--->|Alex |+------+ +------+ +------+
Each node maintains references to both the previous and next nodes.
LinkedList Time Complexity
| Operation | Complexity |
|---|---|
| add(first) | O(1) |
| add(last) | O(1) |
| remove(first) | O(1) |
| remove(last) | O(1) |
| get(index) | O(n) |
| search | O(n) |
LinkedList performs well when frequent insertions and deletions are required.
ArrayList vs LinkedList
| Feature | ArrayList | LinkedList |
|---|---|---|
| Internal Structure | Dynamic Array | Doubly Linked List |
| Random Access | Very Fast | Slow |
| Insert/Delete (Middle) | Slow | Faster (after reaching the node) |
| Memory Usage | Lower | Higher (extra references) |
| Best For | Read-heavy workloads | Frequent insertions/deletions |
Vector
Vector is similar to ArrayList, but it is thread-safe because its methods are synchronized.
Example:
Vector<String> vector =new Vector<>();vector.add("Java");
Differences:
| ArrayList | Vector |
|---|---|
| Not synchronized | Synchronized |
| Faster | Slightly slower |
| Preferred for single-threaded use | Legacy thread-safe option |
In modern applications, thread-safe collections from java.util.concurrent are usually preferred over Vector.
Stack
Stack extends Vector and follows the LIFO (Last In, First Out) principle.
Example:
Stack<String> stack =new Stack<>();stack.push("Book");stack.push("Laptop");stack.push("Phone");
Removing:
System.out.println(stack.pop());
Output:
Phone
Stack Operations
| Method | Description |
|---|---|
| push() | Add element |
| pop() | Remove top element |
| peek() | View top element |
| empty() | Check if empty |
| search() | Search element |
Example:
Stack<Integer> stack =new Stack<>();stack.push(10);stack.push(20);System.out.println(stack.peek());
Output:
20
Iterating Through a List
Using Enhanced for Loop:
List<String> names =new ArrayList<>();names.add("A");names.add("B");for(String name : names){System.out.println(name);}
Using Iterator:
Iterator<String> iterator =names.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}
Using ListIterator:
ListIterator<String> iterator =names.listIterator();while(iterator.hasNext()){System.out.println(iterator.next());}
ListIterator supports both forward and backward traversal.
Fail-Fast vs Fail-Safe Iterators
Fail-Fast
Collections like ArrayList and HashMap throw a ConcurrentModificationException if the collection is structurally modified during iteration (except through the iterator's own methods).
Example:
for(String name : names){names.add("New");}
Output:
ConcurrentModificationException
Fail-Safe
Collections such as CopyOnWriteArrayList iterate over a snapshot of the data, allowing safe modifications during iteration.
Example:
CopyOnWriteArrayList<String> list =new CopyOnWriteArrayList<>();
These collections are useful in concurrent environments.
Real-World Example: Shopping Cart
List<String> cart =new ArrayList<>();cart.add("Laptop");cart.add("Mouse");cart.add("Keyboard");for(String item : cart){System.out.println(item);}
Real-World Example: Browser History
Stack<String> history =new Stack<>();history.push("Google");history.push("OpenAI");history.push("GitHub");System.out.println(history.pop());
Output:
GitHub
Best Practices
Program to the List Interface
Prefer:
List<String> names =new ArrayList<>();
Instead of:
ArrayList<String> names =new ArrayList<>();
Choose the Right Implementation
ArrayList→ Frequent readingLinkedList→ Frequent insertion/deletionStack→ LIFO operations
Use Generics
Always specify the generic type to ensure compile-time type safety.
Prefer Deque over Stack
For new applications, use ArrayDeque or another Deque implementation for stack behavior. Stack is a legacy class.
Avoid Unnecessary Synchronization
Do not use Vector unless synchronized behavior is specifically required.
Common Mistakes
Using LinkedList for Frequent Random Access
Accessing elements by index in a LinkedList is slower than in an ArrayList.
Using ArrayList for Heavy Insert/Delete Operations
Repeated insertions or removals in the middle of an ArrayList require shifting elements.
Modifying Collections During Iteration
This often causes a ConcurrentModificationException.
Using Raw Collections
Always use generics to avoid ClassCastException.
Choosing Stack for New Development
Deque implementations are generally recommended instead of Stack.
Hands-on Exercise
Create a Java program that:
- Creates an
ArrayListof employee names. - Demonstrates insertion at different positions.
- Removes elements from the list.
- Searches for a specific element using
contains(). - Creates a
LinkedListand compares insertion performance conceptually. - Uses a
Stackto simulate browser back navigation. - Iterates through a list using:
- Enhanced for
- Iterator
- ListIterator
- Demonstrates a
ConcurrentModificationExceptionand then solves it using an iterator or a concurrent collection.
Summary
The List interface provides an ordered, index-based collection that supports duplicate elements. ArrayList is the preferred choice for fast random access, while LinkedList excels at frequent insertions and deletions. Vector offers synchronized operations but is largely superseded by modern concurrent collections, and Stack provides LIFO behavior, though Deque is generally recommended for new code.
Key Takeaways
Listmaintains insertion order.- Lists allow duplicate elements and null values.
ArrayListuses a dynamic array.LinkedListuses a doubly linked list.Vectoris synchronized and considered a legacy collection.Stackfollows the LIFO principle.Iteratorsupports forward traversal.ListIteratorsupports forward and backward traversal.- Fail-fast iterators detect structural modifications during iteration.
- Prefer
DequeoverStackfor modern Java applications.
Professional Interview Questions
1What is the difference between ArrayList and LinkedList?
Professional Answer
ArrayList is implemented using a dynamic array and provides fast random access with O(1) average time for indexed retrieval. LinkedList is implemented as a doubly linked list, making insertions and deletions at known positions efficient, but indexed access is O(n) because nodes must be traversed sequentially.
Follow-up Questions
- Which implementation uses more memory?
- Which is better for frequent insertions?
Interview Tip: Remember: ArrayList → Fast Reading, LinkedList → Fast Insert/Delete (once the node is reached).
2Why is ArrayList generally preferred over Vector?
Professional Answer
ArrayList is not synchronized, making it faster in single-threaded applications. Vector synchronizes its methods, which adds overhead. In modern Java, thread-safe collections from the java.util.concurrent package are generally preferred over Vector when concurrency is required.
Follow-up Questions
- Is Vector thread-safe?
- Which package provides modern concurrent collections?
Interview Tip: Unless legacy compatibility is required, prefer ArrayList for general use and concurrent collections for multi-threaded scenarios.
3What is the difference between Iterator and ListIterator?
Professional Answer
Iterator supports forward traversal and element removal during iteration. ListIterator, available only for List implementations, supports both forward and backward traversal, provides index information, and allows adding, updating, and removing elements while iterating.
Follow-up Questions
- Can ListIterator be used with a Set?
- Which methods allow backward traversal?
Interview Tip: Remember: Iterator → All Collections, Forward Only; ListIterator → Lists Only, Forward + Backward.