Home » Java Tutorial » List in Java Collection Framework: A Complete Guide

List in Java Collection Framework: A Complete Guide

If you’re learning Java and want to understand how to store and manage a group of elements (like a list of names, numbers, or objects), then List from the Java Collection Framework is one of the most important tools you’ll need.

In this tutorial, we’ll learn step-by-step through the List interface, its types, methods, usage examples, and common pitfalls.

What is a List in Java?

In Java, a List is a part of the Java Collection Framework and is defined in the java.util package.
It represents an ordered collection of elements — meaning the elements are stored in the order they were added.

A list:

  • Provides index-based access
  • Allows duplicate elements
  • Maintains insertion order

Real-life Analogy:

Imagine a list of students in a classroom. Each student has a fixed roll number (index), and duplicate names are allowed. You can also insert or remove students from the list.

Key Characteristics of List

  • Ordered: Elements maintain insertion order means Elements are stored in the same order they are added
  • Index-based access: You can access elements using an index (starting from 0).
  • Duplicates allowed: You can store the same value multiple times.
  • Dynamic size: Size can grow or shrink dynamically as elements are added or removed

Where Does List Fit in Java Collection Framework?

Here’s the hierarchy:

java.lang.Object  
  ↳ java.util.Collection  
        ↳ java.util.List

List is a subinterface of Collection, and it has several concrete classes:

  • ArrayList
  • LinkedList
  • Vector
  • Stack

Commonly Used List Implementations

Java provides multiple ways to use List:

  • Stack – A subclass of Vector that works on LIFO principle.
  • ArrayList – Backed by a dynamic array. Fast for access, slower for inserts/removals in the middle.
  • LinkedList – Backed by a doubly linked list. Better for insertions/deletions.
  • Vector – Like ArrayList, but synchronized (thread-safe).

Here Brief Explanations of List Implementations

1. Stack

  • A subclass of Vector.
  • Follows LIFO (Last In, First Out) principle.
  • Useful for undo features, expression evaluation, etc.

Example: Using Stack – LIFO (Last In, First Out)

Problem: Simulate browser back button using a Stack.

Java Code:

import java.util.Stack;

public class BrowserHistory {
    public static void main(String[] args) {
        Stack<String> history = new Stack<>();

        // Simulating visiting web pages
        history.push("Home");
        history.push("About");
        history.push("Services");
        history.push("Contact");

        System.out.println("Current page: " + history.peek());

        // Going back (popping last visited pages)
        System.out.println("Going back to: " + history.pop());
        System.out.println("Now on page: " + history.peek());
    }
}

Output:

Current page: Contact
Going back to: Contact
Now on page: Services

Explanation:

  • Stack uses push() to add pages and pop() to go back.
  • peek() shows the current page.
  • Follows LIFO: the last visited page is the first one removed.

2. ArrayList

  • Backed by a dynamic array.
  • Fast for index-based access.
  • Slower for insertions and deletions in the middle.
  • Not thread-safe.

Example 2: Using ArrayList – Fast Access, Dynamic Size

Problem: Store and display a list of student names using ArrayList.

Java Code:

import java.util.ArrayList;

public class StudentList {
    public static void main(String[] args) {
        ArrayList<String> students = new ArrayList<>();

        students.add("Aman");
        students.add("Priya");
        students.add("Rohit");

        // Displaying students
        for (int i = 0; i < students.size(); i++) {
            System.out.println("Student " + (i + 1) + ": " + students.get(i));
        }
    }
}

Output:

Student 1: Aman
Student 2: Priya
Student 3: Rohit

Explanation:

  • ArrayList provides index-based access using get(i).
  • It dynamically resizes as elements are added.
  • Best for read-heavy operations.

3. LinkedList

  • Backed by a doubly linked list.
  • Efficient for frequent insertions and deletions, especially at the beginning or middle.
  • Slower than ArrayList for random access.

Example 3: Using LinkedList – Fast Insert/Delete

Problem: Simulate a print queue where documents are added and printed.

Java Code:

import java.util.LinkedList;

public class PrintQueue {
    public static void main(String[] args) {
        LinkedList<String> printQueue = new LinkedList<>();

        printQueue.add("Document1");
        printQueue.add("Document2");
        printQueue.add("Document3");

        // Printing (removing from front)
        while (!printQueue.isEmpty()) {
            System.out.println("Printing: " + printQueue.removeFirst());
        }
    }
}

Output:

Printing: Document1
Printing: Document2
Printing: Document3

Explanation:

  • LinkedList is ideal for queue-like behavior (FIFO).
  • removeFirst() efficiently removes from the front.
  • Great for real-time systems or messaging apps.

4. Vector

  • Similar to ArrayList but synchronized, making it thread-safe.
  • Slightly slower than ArrayList due to synchronization overhead.

Example 4: Using Vector – Thread-Safe List

Problem: Maintain a thread-safe log of user actions using Vector.

Java Code:

import java.util.Vector;

public class UserActionLog {
    public static void main(String[] args) {
        Vector<String> actions = new Vector<>();

        actions.add("Login");
        actions.add("Upload File");
        actions.add("Logout");

        for (String action : actions) {
            System.out.println("Action: " + action);
        }
    }
}

Output:

Action: Login
Action: Upload File
Action: Logout

Explanation:

  • Vector is similar to ArrayList but is synchronized.
  • Safe to use in multi-threaded environments.
  • Suitable when multiple threads access/modify the list concurrently.

Summary Table:

ImplementationKey FeatureBest Use Case
StackLIFO, Undo operationsNavigating pages, undo, backtracking
ArrayListFast accessStudent lists, records, UI elements
LinkedListFast insertion/removalQueues, dynamic data manipulation
VectorThread-safeLogs in multi-threaded environments

How to Use a List in Java

Let’s go step by step.

1. Import the required classes

import java.util.List;
import java.util.ArrayList;

2. Create a List

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

Always use List as the reference type (interface) and the concrete class like ArrayList or LinkedList on the right side.

Basic List Operations with Examples

Let’s assume we have a list of names using an ArrayList:

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

1. Add Elements

names.add("Alice");
names.add("Bob");
names.add("Charlie");
names.add("Alice");  // duplicate allowed

What it does:

  • Adds elements to the list in the order you specify.
  • Duplicates are allowed in a List.

After adding:

System.out.println(names);

Output:

[Alice, Bob, Charlie, Alice]

2. Access Elements

System.out.println(names.get(0)); // Alice
System.out.println(names.get(2)); // Charlie

What it does:

  • Accesses elements by their index (position in the list).
  • Index starts at 0, so get(0) returns the first element.

Output:

Alice
Charlie

3. Update Elements

names.set(1, "Bobby"); // replaces Bob with Bobby

What it does:

  • Replaces the element at index 1 (which is “Bob”) with “Bobby”.

After updating:

System.out.println(names); 

Output:

[Alice, Bobby, Charlie, Alice]

4. Remove Elements

names.remove(2); // removes "Charlie"

What it does:

  • Removes the element at index 2.
  • The list automatically shifts the remaining elements to fill the gap.

After removal:

System.out.println(names); 

Output:

[Alice, Bobby, Alice]

5. Check for Existence

System.out.println(names.contains("Alice")); // true
System.out.println(names.contains("Tom"));   // false

What it does:

  • Checks if the list contains a certain element.
  • Returns a boolean: true if present, false otherwise.

Output:

true
false

6. Get Size

System.out.println(names.size()); // 3

What it does:

  • Returns the number of elements currently in the list.

Output:

3

7. Clear All Elements

names.clear(); // removes all elements

What it does:

  • Removes all elements from the list.
  • After this, the list becomes empty.

After clearing:

System.out.println(names); 

Output:

[]

8. Check if Empty

System.out.println(names.isEmpty()); // true

What it does:

  • Returns true if the list contains no elements.
  • Useful for checking before performing operations.

Output:

true

list Operations Summary Table:

OperationCode ExampleDescription
Addadd("Alice")Adds element at the end
Accessget(0)Gets element at index 0
Updateset(1, "Bobby")Replaces element at index 1
Removeremove(2)Removes element at index 2
Check Existencecontains("Alice")Returns true if “Alice” exists
Get Sizesize()Returns number of elements
Clear Allclear()Removes all elements
Check if EmptyisEmpty()Returns true if list is empty

Iterating Over a List

Let’s say we have the following list:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

We’ll explore four ways to loop through this list and print each name.

1. Using For Loop

for (int i = 0; i < names.size(); i++) {
    System.out.println(names.get(i));
}

Explanation:

  • Uses an index i that starts from 0.
  • Continues looping until i < names.size().
  • Accesses elements using get(i).

Pros:

  • Gives full control over index.
  • Useful when you need to know or modify the index.

Cons:

  • Slightly more verbose.
  • Not ideal if you just want to read each element.

Output:

Alice
Bob
Charlie

2. Using Enhanced For Loop

for (String name : names) {
    System.out.println(name);
}

Explanation:

  • Simplified loop introduced in Java 5.
  • Automatically loops through all elements in the list.
  • name is a temporary variable representing each element.

Pros:

  • Cleaner syntax.
  • Best for read-only access.
  • No need to worry about indexes.

Cons:

  • Can’t remove elements directly from the list inside this loop.
  • No index access.

Output:

Alice
Bob
Charlie

3. Using Iterator

import java.util.Iterator;

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

Explanation:

  • Iterator is an object that lets you traverse collections one element at a time.
  • hasNext() checks if there’s another element.
  • next() returns the next element.

Pros:

  • Can safely remove elements during iteration using it.remove().
  • Works for any collection type (List, Set, etc.).

Cons:

  • Slightly more complex syntax.
  • More boilerplate code compared to enhanced for-loop.

Output:

Alice
Bob
Charlie

4. Using forEach (Java 8+)

names.forEach(System.out::println);

Explanation:

  • Introduced in Java 8 with Lambda Expressions.
  • forEach() is a method from the Iterable interface.
  • System.out::println is a method reference that prints each item.

Pros:

  • Very concise and readable.
  • Perfect for functional programming style.
  • Can be used with streams, too.

Cons:

  • Not ideal if you need index or want to break the loop.
  • Not for removing elements during iteration.

Output:

Alice
Bob
Charlie

Summary Table of Iterating Over a List

MethodSyntax ExampleSupports RemovalSupports IndexJava Version
For Loopfor (int i = 0; i < list.size(); i++)Java 1+
Enhanced For Loopfor (String s : list)Java 5+
Iteratorwhile (it.hasNext())✅ (via remove())Java 1.2+
forEach (Lambda)list.forEach(System.out::println)Java 8+

List Program 1: Java Program to Store and Display Student Marks Using List

Problem Statement:

Write a Java program to store the names and marks of students in two separate Lists and print each student’s name with their corresponding marks.

Java code

import java.util.*;

public class StudentMarks {
    public static void main(String[] args) {
        // List to store student names
        List<String> studentNames = new ArrayList<>();
        // List to store corresponding marks
        List<Integer> studentMarks = new ArrayList<>();

        // Adding data
        studentNames.add("Aman");
        studentMarks.add(85);

        studentNames.add("Priya");
        studentMarks.add(92);

        studentNames.add("Ravi");
        studentMarks.add(78);

        // Display names and marks
        System.out.println("Student Report:");
        for (int i = 0; i < studentNames.size(); i++) {
            System.out.println(studentNames.get(i) + " scored " + studentMarks.get(i) + " marks.");
        }
    }
}

Output

Student Report:
Aman scored 85 marks.
Priya scored 92 marks.
Ravi scored 78 marks.

Explanation:

  • Two lists are used: studentNames and studentMarks.
  • The ith index in both lists refers to the same student.
  • The for loop uses size() and get(i) to print data together.
  • This shows how parallel lists can be used to manage related information.

List Program 2: Java Program to Remove Duplicate Items from a List

Problem Statement:

Write a Java program that accepts a list of items (with possible duplicates) and removes the duplicate items, preserving the original order.

Java Code:

import java.util.*;

public class RemoveDuplicates {
    public static void main(String[] args) {
        // Initial list with duplicates
        List<String> items = new ArrayList<>();
        items.add("Pen");
        items.add("Pencil");
        items.add("Eraser");
        items.add("Pen");
        items.add("Sharpener");
        items.add("Pencil");

        System.out.println("Original List: " + items);

        // Remove duplicates while maintaining order
        List<String> uniqueItems = new ArrayList<>();
        for (String item : items) {
            if (!uniqueItems.contains(item)) {
                uniqueItems.add(item);
            }
        }

        System.out.println("List after removing duplicates: " + uniqueItems);
    }
}

Output:

Original List: [Pen, Pencil, Eraser, Pen, Sharpener, Pencil]
List after removing duplicates: [Pen, Pencil, Eraser, Sharpener]

Explanation:

  • The program starts with a list that may contain duplicate entries.
  • A second list uniqueItems is created to hold only unique elements.
  • The contains() method checks if an item already exists before adding.
  • This approach maintains the original insertion order.

Differences Between ArrayList vs LinkedList

FeatureArrayListLinkedList
Backed byDynamic ArrayDoubly Linked List
Access SpeedFast (O(1))Slow (O(n))
Insertion/DeletionSlow (O(n))Fast (O(1) at ends)
Memory UsageLessMore

Common Mistakes to Avoid While Using Lists in Java

1. IndexOutOfBoundsException

What it is:
This error occurs when you try to access an index that doesn’t exist in the list.

Example:

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");

System.out.println(list.get(2)); // ❌ Error! Only 0 and 1 are valid indices

🧠 Why it happens:

  • List indices start from 0.
  • If the list size is 2, valid indices are 0 and 1.
  • Accessing index 2 results in IndexOutOfBoundsException.

How to avoid:

  • Always check the size before accessing:
if (index < list.size()) {
    System.out.println(list.get(index));
}

❌ 2. ConcurrentModificationException

What it is:
This occurs when you modify the list (add/remove) while looping over it using a for-each loop or Iterator.

Example (incorrect):

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

for (String fruit : list) {
    if (fruit.equals("Banana")) {
        list.remove(fruit); // ❌ throws ConcurrentModificationException
    }
}

Correct way using Iterator:

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    if (it.next().equals("Banana")) {
        it.remove(); // ✅ safe removal during iteration
    }
}

🧠 Why it happens:

  • Internally, the iterator expects no structural changes while iterating.
  • Direct list modification breaks that expectation.

❌ 3. Confusing remove(index) vs remove(Object)

In Java Lists, the remove() method is overloaded:

  • remove(int index) — removes the element at a given index.
  • remove(Object o) — removes the first occurrence of the given object.

🔍 Example (confusing case):

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

list.remove(1); // ❗ Removes element at index 1 (i.e., value 2), not value 1
System.out.println(list);

Output:

[1, 3]

Correct way to remove value 1:

list.remove(Integer.valueOf(1)); // removes the object `1` (not index 1)

🧠 Why it happens:

  • When you pass a number to remove(), Java assumes it’s an index unless you explicitly cast or use Integer.valueOf().

Summary Table

MistakeWhat It MeansExampleHow to Fix
IndexOutOfBoundsExceptionAccessing non-existent indexlist.get(10) when size is 5Use list.size() to check bounds
ConcurrentModificationExceptionModifying list during iterationRemoving in a for-each loopUse Iterator.remove()
remove(index) vs remove(Object)Misunderstanding method overloadslist.remove(1) removes index 1Use Integer.valueOf(1) to remove value

When Should You Use a List in Java?

The List interface in Java is a powerful and flexible data structure that is part of the Java Collections Framework. But how do you know when it’s the right choice to use a List?

Below are the most common use cases that clearly explain when and why to use a List in your Java application:

1. When You Want to Store Data in Insertion Order

Unlike some other collections like Set or Map, a List preserves the order in which elements are inserted.

Example:

List<String> tasks = new ArrayList<>();
tasks.add("Wake up");
tasks.add("Brush teeth");
tasks.add("Go for a walk");
System.out.println(tasks); 

Output:

[Wake up, Brush teeth, Go for a walk]

Why it matters:

  • Useful for things like task lists, logs, playlists, and queues where order is important.

2. When You Want to Allow Duplicate Values

A List allows you to store the same value multiple times. This is helpful in scenarios where repeated entries are meaningful.

Example:

List<String> votes = new ArrayList<>();
votes.add("Alice");
votes.add("Bob");
votes.add("Alice"); // duplicate
System.out.println(votes); 

Output:

Alice, Bob, Alice]

Why it matters:

  • Perfect for storing items like votes, form submissions, or scores that may naturally repeat.

3. When You Need to Access Elements by Index

Lists provide zero-based indexing, meaning you can quickly get, set, or remove an element using its index.

Example:

List<String> fruits = Arrays.asList("Apple", "Banana", "Mango");
System.out.println(fruits.get(1)); 

Output:

Banana

Why it matters:

  • Ideal for use cases like accessing specific rows in a table, elements in a UI component, or characters in a sentence list.

4. When You Want a Dynamically Resizable Collection

Unlike arrays, Lists are dynamic — they can grow or shrink as needed. You don’t need to declare the size in advance.

Example:

List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.remove(0); // Remove first element
System.out.println(numbers); // Output: [20]

Why it matters:

  • Saves memory and reduces complexity when you’re dealing with unpredictable or frequently changing datasets (e.g., real-time chat, shopping cart, dynamic forms).

Use List When:

  • Maintaining order of data: List keeps elements in insertion order
  • Allowing duplicates: List permits repeating elements
  • Accessing elements by position: Use get(index), set(index, value)
  • Flexible size requirements: List can grow and shrink as needed

When Not to Use a List:

  • If order doesn’t matter, consider using a Set.
  • If you need key-value mapping, use a Map.
  • If you need constant-time lookups, and no duplicates, use HashSet.

Practice Questions

  1. Write a Java program to take 5 names from the user and store them in a list.
  2. Write a program to count how many times a name appears in a list.
  3. Write a program to sort a list of integers in ascending and descending order.
  4. Create a to-do list app using ArrayList with add, remove, and display features.

Conclusion

The List interface is a foundational concept in Java that every beginner must understand thoroughly. Whether you’re working on a student attendance system, a product catalog, or a playlist app — Lists make it easy to manage ordered collections of data.

❓Frequently Asked Questions (FAQs) About List in Java

Q1. What is the difference between ArrayList and LinkedList?

Answer:

  • ArrayList is backed by a dynamic array, offering fast access via index but slower insertions/deletions in the middle.
  • LinkedList is backed by a doubly-linked list, making it faster for insertions and deletions, especially in large lists.

Q2. Can a Java List contain duplicate elements?

Answer:
Yes, a List allows duplicate values. You can add the same element multiple times, and it will be stored as separate entries.

List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Alice"); // Allowed

Q3. What happens if I access an invalid index in a List?

Answer:
If you try to access an index that doesn’t exist, Java will throw an IndexOutOfBoundsException.

List<Integer> list = Arrays.asList(10, 20);
System.out.println(list.get(5)); // ❌ Error!

Q4. How do I remove a specific element from a List in Java?

Answer:
Use remove(Object o) to remove a specific value and remove(index) to remove by position. For integers, use:

list.remove(Integer.valueOf(10)); // Removes value 10

Q5. How can I iterate through a List in Java?

Answer:
You can iterate using:

  • Traditional for loop
  • Enhanced for-each loop
  • Iterator
  • forEach() method (Java 8+)

Example:

for (String item : list) {
    System.out.println(item);
}

Q6. Can I sort a List in Java?

Answer:
Yes! Use Collections.sort(list) to sort the list in ascending order. For custom sorting, you can pass a comparator.

Collections.sort(list);

Q7. Is List thread-safe in Java?

Answer:
No, by default List implementations like ArrayList are not thread-safe. Use Collections.synchronizedList(list) or CopyOnWriteArrayList for thread-safe operations.

Q8. How do I convert an array to a List in Java?

Answer:
Use Arrays.asList() to convert an array to a fixed-size list.

String[] arr = {"A", "B", "C"};
List<String> list = Arrays.asList(arr);