Index

Lists in Detail

Java Collections

4.1 ArrayList vs LinkedList: Internal workings

Understanding how ArrayList and LinkedList work under the hood is essential for writing efficient Java programs. Although both implement the List interface and provide similar APIs, their internal data structures are fundamentally different, which impacts how they perform under various operations.

ArrayList: Backed by a Dynamic Array

An ArrayList is essentially a resizable array. Internally, it uses an array of Object elements to store its data. When you add elements to an ArrayList, they are placed into consecutive slots in the internal array.

How it Works:

Key Operations:

Analogy:

Think of an ArrayList like a tray of eggs — each egg slot (index) is fixed and next to the other. If you need to insert a new egg between two existing ones, you have to move all eggs after it to make space.

LinkedList: Doubly Linked Nodes

A LinkedList is made up of nodes — each node contains a data element and two references:

This structure is known as a doubly linked list.

How it Works:

Key Operations:

Analogy:

Imagine a train with carriages (nodes) linked together. To reach the 10th carriage, you must walk from the front or back. But adding or removing a carriage at either end is easy — just detach or attach it.

Memory Layout and Overhead

Resizing vs. Linking

Visual Diagrams (Conceptual)

ArrayList:

[Index]   0     1     2     3     4
         [A]   [B]   [C]   [D]   [E]
         ↑
     Contiguous array in memory

LinkedList:

null ← [A] ↔ [B] ↔ [C] ↔ [D] ↔ [E] → null
     Each node holds value + links

Conclusion

Understanding these internal structures helps you make better choices for performance-sensitive applications.

Index

4.2 When to use which List?

Choosing between ArrayList and LinkedList depends on how your application interacts with data. Both implement the List interface but differ significantly in performance, memory usage, and ideal use cases due to their internal structures.

Let’s explore when each is most appropriate, supported by practical scenarios and performance considerations.

Use ArrayList When:

You Need Fast Random Access

If your program frequently accesses elements by index, ArrayList is ideal. It supports constant-time access (O(1)) because it’s backed by a dynamic array.

Example:

List<String> list = new ArrayList<>();
String item = list.get(5000); // Fast access

Use case: Displaying data in UI tables, managing cached items by index.

You Mostly Add Elements at the End

Appending elements is fast and efficient with ArrayList, especially if you don’t reach the current capacity often. Resizing does occur, but it’s amortized and generally efficient.

Example:

list.add("New Item"); // Typically O(1)

Use case: Logging, collecting API responses, building a list of search results.

Memory Efficiency Matters

Each element in an ArrayList only takes space for the object itself (plus array overhead), whereas LinkedList stores additional references for previous and next nodes.

Use LinkedList When:

You Frequently Insert/Delete at the Beginning or Middle

If your use case involves many insertions or deletions at the start or middle of the list, LinkedList avoids expensive array shifting operations, making these operations more efficient — O(1) at the ends, O(n) in the middle but faster than ArrayList when removing many elements.

Example:

list.add(0, "Urgent Task"); // O(1) for LinkedList

Use case: Task scheduling queues, history stacks, undo functionality.

You Need to Traverse and Modify via Iterators

With LinkedList, iterators can be used to efficiently add or remove elements during traversal without incurring large shifts in data.

Use case: Editing live data streams, streaming event handlers.

Performance Summary Table

Operation ArrayList LinkedList
Random Access (get/set) O(1) O(n)
Add at End O(1)* O(1)
Insert/Delete at Middle/Start O(n) O(n) / O(1)
Memory per element Lower Higher (more overhead)
Iterator-based Deletion Slower Faster

*Amortized O(1), may resize when capacity is exceeded.

Final Advice

By matching the list type to your use case, you’ll improve your program’s efficiency, readability, and memory footprint.

Index

4.3 Common Operations and Performance Considerations

Java’s List interface defines a set of common operations used to manage sequences of elements. While the interface stays the same, the performance of these operations differs significantly depending on whether the underlying implementation is an ArrayList or a LinkedList.

Let’s explore these operations with explanations, time complexity, and examples to help you make informed decisions based on performance.

Adding Elements

List<String> list = new ArrayList<>();
list.add("One");  // Fast, unless resizing
list.add(0, "Zero");  // Slower for large ArrayLists

Removing Elements

list.remove("One");  // Linear time for both

Accessing and Modifying Elements

list.set(0, "Updated");  // Fast for ArrayList
String item = list.get(2);  // Instant with ArrayList, slow with LinkedList

Checking for Containment

if (list.contains("Updated")) {
    System.out.println("Found!");
}

Both lists must scan through the elements linearly, as they don't use hashing like Set.

Iterating Through Elements

for (String item : list) {
    System.out.println(item);
}
ListIterator<String> it = list.listIterator();
while (it.hasNext()) {
    if (it.next().equals("Updated")) {
        it.set("Modified");
    }
}

Summary Table of Time Complexities

Operation ArrayList LinkedList
add(E) O(1)* O(1)
add(index, E) O(n) O(n)
remove(Object) O(n) O(n)
remove(index) O(n) O(n)
get(index) O(1) O(n)
set(index, E) O(1) O(n)
contains(Object) O(n) O(n)
iterator traversal O(n) O(n)

*Amortized, due to occasional resizing.

Performance Considerations

Understanding these trade-offs helps you write efficient, purpose-driven Java programs using the right List for your specific use case.

Index

4.4 Runnable Examples: Add, remove, sort, search in Lists

This section demonstrates how to perform common operations—add, remove, sort, and search—on Java ArrayList and LinkedList using concise, runnable code examples. Each snippet is explained to reinforce both syntax and behavior.

Example 1: Adding Elements to ArrayList and LinkedList

import java.util.*;

public class ListOperationsDemo {
    public static void main(String[] args) {
        // Creating an ArrayList
        List<String> arrayList = new ArrayList<>();
        arrayList.add("Apple");
        arrayList.add("Banana");
        arrayList.add("Cherry");

        // Creating a LinkedList
        List<String> linkedList = new LinkedList<>();
        linkedList.add("Dog");
        linkedList.add("Elephant");
        linkedList.add("Fox");

        System.out.println("ArrayList: " + arrayList);
        System.out.println("LinkedList: " + linkedList);
    }
}

Explanation: add() appends elements to the end of both ArrayList and LinkedList. The syntax is identical because both implement the List interface.

Example 2: Removing Elements

arrayList.remove("Banana"); // Removes "Banana" by value
        linkedList.remove(1);       // Removes element at index 1 ("Elephant")

        System.out.println("ArrayList after removal: " + arrayList);
        System.out.println("LinkedList after removal: " + linkedList);

Explanation: remove(Object o) deletes by value, while remove(int index) deletes by position. These operations shift or unlink nodes depending on the list type.

Example 3: Sorting Lists with Collections.sort() and Custom Comparator

// Sorting ArrayList alphabetically
        Collections.sort(arrayList);
        System.out.println("Sorted ArrayList: " + arrayList);

        // Sorting LinkedList in reverse order using a Comparator
        linkedList.sort(Comparator.reverseOrder());
        System.out.println("Reverse sorted LinkedList: " + linkedList);

Explanation: Collections.sort() sorts in natural (lexicographical) order. You can also use .sort(Comparator) for custom ordering. Both list types support sorting via the List interface.

Example 4: Searching Lists (contains and binarySearch)

// Using contains (linear search)
        boolean found = arrayList.contains("Apple");
        System.out.println("Contains 'Apple'? " + found); // true

        // Using binarySearch (list must be sorted)
        int index = Collections.binarySearch(arrayList, "Cherry");
        System.out.println("'Cherry' found at index: " + index); // Index >= 0 if found

Explanation:

Output Summary

ArrayList: [Apple, Banana, Cherry]
LinkedList: [Dog, Elephant, Fox]
ArrayList after removal: [Apple, Cherry]
LinkedList after removal: [Dog, Fox]
Sorted ArrayList: [Apple, Cherry]
Reverse sorted LinkedList: [Fox, Dog]
Contains 'Apple'? true
'Cherry' found at index: 1

Final Notes

These examples illustrate practical List usage with operations that appear in real-world applications. You can modify and run them as-is to reinforce learning.

Index