Java Tutorial 0/145 lessons ~6 min read Lesson 25

    List Interface

    java list interface ArrayList LinkedList Vector Stack ListIterator fail-fast ConcurrentModificationException Deque

    Course progress0%
    Focus
    25 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    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:

    code
    List<String> fruits = new ArrayList<>();
    fruits.add("Apple");
    fruits.add("Banana");
    fruits.add("Apple");

    Output:

    code
    Apple
    Banana
    Apple

    Notice that duplicates are allowed.

    Characteristics of List

    A List:

    • Maintains insertion order
    • Allows duplicate elements
    • Supports indexing
    • Allows null values
    • Supports iteration

    Example:

    code
    List<Integer> numbers = new ArrayList<>();
    numbers.add(10);
    numbers.add(20);
    numbers.add(30);
    System.out.println(numbers.get(1));

    Output:

    code
    20

    List Hierarchy

    code
    Iterable
    Collection
    List
    ┌──┼──────────┐
    │ │ │
    ArrayList LinkedList
    Vector
    Stack

    Common List Methods

    code
    add()
    add(index, element)
    get()
    set()
    remove()
    contains()
    indexOf()
    lastIndexOf()
    size()
    isEmpty()
    clear()

    Example:

    code
    List<String> names = new ArrayList<>();
    names.add("Rahul");
    names.add("Amit");
    System.out.println(names.size());

    Output:

    code
    2

    ArrayList

    ArrayList is the most commonly used implementation of the List interface.

    Internally, it uses a dynamic array.

    Example:

    code
    List<String> cities =
    new ArrayList<>();
    cities.add("Delhi");
    cities.add("Mumbai");
    cities.add("Hyderabad");

    Internal Working of ArrayList

    Memory:

    code
    +---------+---------+---------+---------+
    | 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

    OperationComplexity
    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:

    code
    List<String> employees =
    new LinkedList<>();
    employees.add("John");
    employees.add("David");

    LinkedList Memory Representation

    code
    +------+ +------+ +------+
    |John |<--->|David |<--->|Alex |
    +------+ +------+ +------+

    Each node maintains references to both the previous and next nodes.

    LinkedList Time Complexity

    OperationComplexity
    add(first)O(1)
    add(last)O(1)
    remove(first)O(1)
    remove(last)O(1)
    get(index)O(n)
    searchO(n)

    LinkedList performs well when frequent insertions and deletions are required.

    ArrayList vs LinkedList

    FeatureArrayListLinkedList
    Internal StructureDynamic ArrayDoubly Linked List
    Random AccessVery FastSlow
    Insert/Delete (Middle)SlowFaster (after reaching the node)
    Memory UsageLowerHigher (extra references)
    Best ForRead-heavy workloadsFrequent insertions/deletions

    Vector

    Vector is similar to ArrayList, but it is thread-safe because its methods are synchronized.

    Example:

    code
    Vector<String> vector =
    new Vector<>();
    vector.add("Java");

    Differences:

    ArrayListVector
    Not synchronizedSynchronized
    FasterSlightly slower
    Preferred for single-threaded useLegacy 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:

    code
    Stack<String> stack =
    new Stack<>();
    stack.push("Book");
    stack.push("Laptop");
    stack.push("Phone");

    Removing:

    code
    System.out.println(stack.pop());

    Output:

    code
    Phone

    Stack Operations

    MethodDescription
    push()Add element
    pop()Remove top element
    peek()View top element
    empty()Check if empty
    search()Search element

    Example:

    code
    Stack<Integer> stack =
    new Stack<>();
    stack.push(10);
    stack.push(20);
    System.out.println(stack.peek());

    Output:

    code
    20

    Iterating Through a List

    Using Enhanced for Loop:

    code
    List<String> names =
    new ArrayList<>();
    names.add("A");
    names.add("B");
    for(String name : names){
    System.out.println(name);
    }

    Using Iterator:

    code
    Iterator<String> iterator =
    names.iterator();
    while(iterator.hasNext()){
    System.out.println(iterator.next());
    }

    Using ListIterator:

    code
    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:

    code
    for(String name : names){
    names.add("New");
    }

    Output:

    code
    ConcurrentModificationException

    Fail-Safe

    Collections such as CopyOnWriteArrayList iterate over a snapshot of the data, allowing safe modifications during iteration.

    Example:

    code
    CopyOnWriteArrayList<String> list =
    new CopyOnWriteArrayList<>();

    These collections are useful in concurrent environments.

    Real-World Example: Shopping Cart

    code
    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

    code
    Stack<String> history =
    new Stack<>();
    history.push("Google");
    history.push("OpenAI");
    history.push("GitHub");
    System.out.println(history.pop());

    Output:

    code
    GitHub

    Best Practices

    Program to the List Interface

    Prefer:

    code
    List<String> names =
    new ArrayList<>();

    Instead of:

    code
    ArrayList<String> names =
    new ArrayList<>();

    Choose the Right Implementation

    • ArrayList → Frequent reading
    • LinkedList → Frequent insertion/deletion
    • Stack → 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:

    1. Creates an ArrayList of employee names.
    2. Demonstrates insertion at different positions.
    3. Removes elements from the list.
    4. Searches for a specific element using contains().
    5. Creates a LinkedList and compares insertion performance conceptually.
    6. Uses a Stack to simulate browser back navigation.
    7. Iterates through a list using:
      • Enhanced for
      • Iterator
      • ListIterator
    8. Demonstrates a ConcurrentModificationException and 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

    • List maintains insertion order.
    • Lists allow duplicate elements and null values.
    • ArrayList uses a dynamic array.
    • LinkedList uses a doubly linked list.
    • Vector is synchronized and considered a legacy collection.
    • Stack follows the LIFO principle.
    • Iterator supports forward traversal.
    • ListIterator supports forward and backward traversal.
    • Fail-fast iterators detect structural modifications during iteration.
    • Prefer Deque over Stack for 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.

    Ready to mark this lesson complete?Track your journey across the entire course.