☕ Java

Java ArrayList Class

A complete guide to the Java ArrayList class — dynamic resizing, generics, CRUD operations, sorting, iteration methods, ArrayList vs Array vs LinkedList vs Vector, nested ArrayList, and best practices with real-world examples.

📅

Last Updated

March 2026

⏱️

Read Time

18 min

🎯

Level

Beginner to Intermediate

What is the Java ArrayList Class?

The ArrayList class in Java is a part of the java.util package and provides a resizable (dynamic) array implementation of the List interface. It is the most widely used collection class in Java — the go-to choice whenever you need an ordered, index-accessible list of elements whose size is not known in advance.

Unlike a plain Java array — which has a fixed size set at creation — an ArrayList automatically grows its internal array when elements are added beyond current capacity, and can shrink it when needed. It preserves insertion order, allows duplicate elements, and permits null values. All elements are accessible by zero-based integer index in O(1) time.

Think of an ArrayList as a smart, elastic shelf. You can keep adding books (elements) to it — when it runs out of space, it quietly moves everything to a bigger shelf. You can reach any book instantly by its position number (index), and the shelf always knows exactly how many books it holds.

ArrayList Syntax and Constructors

The ArrayList class provides three constructors. Always use generics (ArrayList<Type>) to ensure type safety and avoid raw-type warnings.

☕ JavaArrayList Constructors
import java.util.ArrayList;
import java.util.List;

// 1. Default constructor — internal capacity starts at 10
ArrayList<String> list1 = new ArrayList<>();

// 2. Constructor with initial capacity — avoids early resize
ArrayList<Integer> list2 = new ArrayList<>(50);

// 3. Constructor from existing Collection — copies all elements
List<String> source = List.of("Java", "Python", "Kotlin");
ArrayList<String> list3 = new ArrayList<>(source);

// Best practice: declare using List interface (program to interface)
List<String> languages = new ArrayList<>();
☕ JavaArrayList — First Program
import java.util.ArrayList;
import java.util.List;

public class ArrayListIntro {
    public static void main(String[] args) {

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

        // Add elements
        fruits.add("Mango");
        fruits.add("Banana");
        fruits.add("Papaya");
        fruits.add("Guava");
        fruits.add("Mango");   // duplicates allowed
        fruits.add(null);        // null allowed

        System.out.println("List   : " + fruits);
        System.out.println("Size   : " + fruits.size());
        System.out.println("Index 1: " + fruits.get(1));
        System.out.println("Has Banana? " + fruits.contains("Banana"));
    }
}

Output

List : [Mango, Banana, Papaya, Guava, Mango, null] Size : 6 Index 1: Banana Has Banana? true

Why Use ArrayList?

ArrayList is the default choice for most Java list operations. Here is why it dominates everyday Java development:

  • 📐 Dynamic Size — No need to declare size upfront. ArrayList grows on demand. This eliminates ArrayIndexOutOfBoundsException from under-sized fixed arrays and memory waste from over-sized ones.

  • ⚡ Fast Random Access — Backed by an array, ArrayList provides O(1) get(index) and set(index) — as fast as a plain array. Perfect for workloads that read by index far more than they insert or delete.

  • 🔗 Full List Interface — ArrayList implements java.util.List completely, making it compatible with Collections utility methods (sort, shuffle, binarySearch, reverse, frequency, disjoint) and all Java APIs expecting a List.

  • 🔁 Multiple Iteration Styles — Supports for-each, traditional for loop, Iterator, ListIterator, and Java 8+ streams — giving flexibility to choose the best traversal approach for each situation.

  • 🧬 Generics Support — ArrayList<T> enforces type safety at compile time. You cannot accidentally add a String to an ArrayList<Integer>, catching bugs before runtime.

  • 🏗️ Rich API — Built-in methods for add, remove, search (contains, indexOf, lastIndexOf), bulk operations (addAll, removeAll, retainAll, containsAll), subList, and conversion to array.

  • 🌐 Framework Integration — Virtually every Java framework (Spring, Hibernate, Jackson, JAX-RS) works with List and returns or accepts ArrayList. It is the universal currency of Java data passing.

ArrayList vs Array vs LinkedList vs Vector

Choosing the right data structure depends on your access patterns, thread requirements, and whether size is known upfront. This table covers all four common options:

FeatureArrayArrayListLinkedListVector
PackageBuilt-in languagejava.utiljava.utiljava.util
SizeFixed at creationDynamic (resizes)Dynamic (linked nodes)Dynamic (resizes)
Type safetyPrimitive + ObjectObject only (generics)Object only (generics)Object only (generics)
Nulls allowed?✅ Yes✅ Yes✅ Yes✅ Yes
Duplicates?✅ Yes✅ Yes✅ Yes✅ Yes
Random accessO(1) — fastestO(1) — fastO(n) — slowO(1) — fast
Insert at middleNot supported (manual)O(n) — shift neededO(1) — pointer updateO(n) — shift needed
Insert at head/tailNot supportedO(n) head / O(1)* tailO(1) both endsO(n) head / O(1)* tail
Delete at middleNot supported (manual)O(n) — shift neededO(1) — pointer updateO(n) — shift needed
Memory per elementMinimalLow (array slot)High (prev + next pointers)Low (array slot + lock)
Thread-safe?❌ No❌ No❌ No✅ Yes (synchronized)
Implements Deque?❌ No❌ No✅ Yes❌ No
Growth strategyN/A (fixed)~50% growthNo pre-allocationDoubles (or fixed increment)
Best forKnown fixed-size dataGeneral purpose listsFrequent head/tail opsLegacy thread-safe lists

Decision guide: Use ArrayList by default for single-threaded ordered lists. Use LinkedList only if you frequently insert/delete at both ends. Use int[] arrays for primitive numeric data with fixed size. Avoid Vector in new code — use Collections.synchronizedList(new ArrayList<>()) instead.

ArrayList in the Java Collections Hierarchy

Understanding where ArrayList sits in Java's Collections hierarchy clarifies which interfaces it fulfills and which methods it inherits from each level.

java.lang.Iterable<E>
Enables for-each loop support via iterator()
java.util.Collection<E>
add, remove, contains, size, isEmpty, iterator, toArray, addAll, removeAll, retainAll, clear
java.util.List<E>
get, set, add(index), remove(index), indexOf, lastIndexOf, listIterator, subList
java.util.AbstractList<E> → java.util.AbstractCollection<E>
Skeletal implementations — toString, equals, hashCode, iterator, listIterator
java.util.ArrayList<E>
Implements: List<E>, RandomAccess, Cloneable, SerializableBacked by: Object[] elementDataGrowth: newCapacity = oldCapacity + (oldCapacity >> 1) (~50% increase)

Architecture Diagram

RandomAccess marker interface: ArrayList implements RandomAccess — a marker interface signaling that index-based access is O(1). Algorithms in Collections (like binarySearch) check for this interface and use a faster index-based strategy when present, instead of iterating sequentially.

Important ArrayList Methods

ArrayList inherits from List and Collection and also has its own capacity-management methods. Here is the complete method reference:

MethodDescriptionTime Complexity
add(E e)Appends element to endAmortized O(1)
add(int index, E e)Inserts at specific index — shifts rightO(n)
addAll(Collection c)Appends all elements of collection to endO(k)
addAll(int index, Collection c)Inserts collection at specific indexO(n+k)
get(int index)Returns element at index — zero-basedO(1)
set(int index, E e)Replaces element at index, returns old valueO(1)
remove(int index)Removes element at index, returns removed elementO(n)
remove(Object o)Removes first occurrence of elementO(n)
removeAll(Collection c)Removes all elements present in given collectionO(n*k)
retainAll(Collection c)Keeps only elements present in given collectionO(n*k)
clear()Removes all elements — size becomes 0O(n)
size()Returns current number of elementsO(1)
isEmpty()Returns true if size == 0O(1)
contains(Object o)Returns true if element existsO(n)
containsAll(Collection c)Returns true if all elements of c are presentO(n*k)
indexOf(Object o)Returns index of first occurrence, -1 if absentO(n)
lastIndexOf(Object o)Returns index of last occurrence, -1 if absentO(n)
iterator()Returns a fail-fast IteratorO(1)
listIterator()Returns a bidirectional ListIteratorO(1)
listIterator(int index)Returns ListIterator starting at given indexO(1)
subList(int from, int to)Returns a view [from, to) — backed by original listO(1)
toArray()Returns Object[] of all elementsO(n)
toArray(T[] a)Returns typed array of all elementsO(n)
sort(Comparator c)Sorts in-place using given comparator (Java 8+)O(n log n)
replaceAll(UnaryOperator op)Replaces each element with result of operator (Java 8+)O(n)
removeIf(Predicate p)Removes all elements matching predicate (Java 8+)O(n)
forEach(Consumer action)Performs action for each element (Java 8+)O(n)
stream()Returns a sequential Stream over elements (Java 8+)O(1)
ensureCapacity(int min)Ensures internal capacity is at least minO(n) worst case
trimToSize()Reduces capacity to current size — saves memoryO(n)
clone()Returns a shallow copy of the ArrayListO(n)

CRUD Operations on ArrayList

ArrayList supports all four CRUD operations — Create, Read, Update, and Delete — with clean, expressive syntax. Here is a comprehensive demo of every major operation:

☕ JavaComplete CRUD Operations
import java.util.*;

public class ArrayListCRUD {
    public static void main(String[] args) {

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

        // ---- CREATE ----
        students.add("Arjun");
        students.add("Priya");
        students.add("Rohan");
        students.add("Sneha");
        students.add(1, "Vikram");       // insert at index 1
        students.addAll(List.of("Aisha", "Dev")); // bulk add
        System.out.println("After CREATE : " + students);

        // ---- READ ----
        System.out.println("get(0)       : " + students.get(0));
        System.out.println("size()       : " + students.size());
        System.out.println("contains Priya? " + students.contains("Priya"));
        System.out.println("indexOf Rohan : " + students.indexOf("Rohan"));
        System.out.println("subList(1,3) : " + students.subList(1, 3));

        // ---- UPDATE ----
        students.set(0, "Arjun Kumar");  // replace at index 0
        System.out.println("After UPDATE : " + students);

        // ---- DELETE ----
        students.remove("Dev");           // remove by value
        students.remove(2);                // remove by index
        students.removeIf(s -> s.startsWith("A")); // remove by condition
        System.out.println("After DELETE : " + students);

        // ---- BULK OPERATIONS ----
        List<String> toRemove = List.of("Priya", "Sneha");
        students.removeAll(toRemove);
        System.out.println("After removeAll: " + students);
    }
}

Output

After CREATE : [Arjun, Vikram, Priya, Rohan, Sneha, Aisha, Dev] get(0) : Arjun size() : 7 contains Priya? true indexOf Rohan : 3 subList(1,3) : [Vikram, Priya] After UPDATE : [Arjun Kumar, Vikram, Priya, Rohan, Sneha, Aisha, Dev] After DELETE : [Vikram, Rohan, Sneha] After removeAll: [Vikram]

Sorting and Searching an ArrayList

ArrayList integrates seamlessly with Collections utility methods and Java 8+ lambda-based sorting. You can sort in natural order, reverse order, or by any custom comparator.

☕ JavaSorting and Searching ArrayList
import java.util.*;

public class SortSearchDemo {
    public static void main(String[] args) {

        List<Integer> scores = new ArrayList<>(Arrays.asList(78, 45, 92, 61, 88, 34, 99, 55));
        System.out.println("Original : " + scores);

        // Natural (ascending) sort
        Collections.sort(scores);
        System.out.println("Ascending: " + scores);

        // Reverse (descending) sort
        scores.sort(Collections.reverseOrder());
        System.out.println("Descending: " + scores);

        // Binary search (list must be sorted ascending first)
        Collections.sort(scores);
        int idx = Collections.binarySearch(scores, 88);
        System.out.println("binarySearch(88) at index: " + idx);

        // --- Sorting custom objects with Comparator ---
        List<String> names = new ArrayList<>(Arrays.asList("Zara", "Amit", "Riya", "Kabir"));

        // Sort by length, then alphabetically
        names.sort(Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder()));
        System.out.println("Sorted by length: " + names);

        // Reverse alphabetical
        names.sort(Comparator.reverseOrder());
        System.out.println("Reverse alpha: " + names);

        // Min and Max
        System.out.println("Min: " + Collections.min(scores));
        System.out.println("Max: " + Collections.max(scores));
    }
}

Output

Original : [78, 45, 92, 61, 88, 34, 99, 55] Ascending: [34, 45, 55, 61, 78, 88, 92, 99] Descending: [99, 92, 88, 78, 61, 55, 45, 34] binarySearch(88) at index: 6 Sorted by length: [Zara, Amit, Riya, Kabir] Reverse alpha: [Zara, Riya, Kabir, Amit] Min: 34 Max: 99

Iterating an ArrayList — 6 Ways

ArrayList supports six distinct iteration techniques. Each has specific strengths — choosing the right one improves both performance and code clarity.

☕ JavaAll 6 Iteration Methods
import java.util.*;
import java.util.stream.Collectors;

public class IterationDemo {
    public static void main(String[] args) {

        List<String> cities = new ArrayList<>(List.of("Delhi", "Mumbai", "Pune", "Jaipur", "Surat"));

        // 1. Traditional for loop (index-based) — best when index needed
        System.out.println("1. for loop:");
        for (int i = 0; i < cities.size(); i++) {
            System.out.print(cities.get(i) + " ");
        }

        // 2. Enhanced for-each — cleanest for read-only traversal
        System.out.println("\n2. for-each:");
        for (String city : cities) {
            System.out.print(city + " ");
        }

        // 3. Iterator — use when removing elements during traversal
        System.out.println("\n3. Iterator (removes 'Pune'):");
        Iterator<String> it = cities.iterator();
        while (it.hasNext()) {
            String c = it.next();
            if (c.equals("Pune")) it.remove();  // safe removal
            else System.out.print(c + " ");
        }

        // 4. ListIterator — bidirectional, supports add/set during traversal
        System.out.println("\n4. ListIterator (reverse):");
        ListIterator<String> li = cities.listIterator(cities.size());
        while (li.hasPrevious()) {
            System.out.print(li.previous() + " ");
        }

        // 5. forEach with lambda (Java 8+) — concise, functional style
        System.out.println("\n5. forEach lambda:");
        cities.forEach(city -> System.out.print(city + " "));

        // 6. Stream API (Java 8+) — for filter/map/collect pipelines
        System.out.println("\n6. Stream (filter cities with length > 4):");
        List<String> filtered = cities.stream()
            .filter(c -> c.length() > 4)
            .sorted()
            .collect(Collectors.toList());
        System.out.println(filtered);
    }
}

Output

1. for loop: Delhi Mumbai Pune Jaipur Surat 2. for-each: Delhi Mumbai Pune Jaipur Surat 3. Iterator (removes 'Pune'): Delhi Mumbai Jaipur Surat 4. ListIterator (reverse): Surat Jaipur Mumbai Delhi 5. forEach lambda: Delhi Mumbai Jaipur Surat 6. Stream (filter cities with length > 4): [Delhi, Jaipur, Mumbai, Surat]
MethodCan Modify?Bidirectional?Best Use Case
for loop (index)✅ Yes✅ Yes (manual)When you need the index
for-each❌ No (ConcurrentModificationException)❌ NoClean read-only traversal
Iterator✅ Yes (via it.remove())❌ NoSafe removal during traversal
ListIterator✅ Yes (add, set, remove)✅ YesBidirectional + modification
forEach lambda❌ No (effectively final)❌ NoConcise, functional read-only
Stream API❌ No (non-destructive)❌ NoFilter / map / collect pipelines

ArrayList Internal Resize Flow — Flowchart

The flowchart below shows exactly what happens inside ArrayList when you call add(element) — how it decides whether to grow and how it calculates the new capacity.

➕ add(element)called on ArrayList
❓ size < capacity?check elementData.length
Yes — fits
✅ Insert directlyelementData[size++] = e
📐 Calculate new capacitynewCap = oldCap + (oldCap >> 1)
❓ newCap < minCapacity?edge case check
Yes
🔧 Use minCapacitynewCap = minCapacity
🔄 Arrays.copyOf()allocate + copy old elements
✅ Insert elementinto expanded array

Code Execution Flow — from source to output

Growth formula: newCapacity = oldCapacity + (oldCapacity >> 1) — this is an arithmetic right shift by 1, equivalent to adding 50% of the current capacity. So a capacity-10 ArrayList grows to 15, then 22, then 33, then 49, and so on. This amortizes the O(n) copy cost across many insertions, giving add() an amortized O(1) time complexity.

ArrayList with Generics and Custom Objects

One of ArrayList's most powerful capabilities is working with custom objects via generics. You can store, sort, filter, and manipulate any user-defined class in a type-safe ArrayList.

☕ JavaArrayList of Custom Objects
import java.util.*;
import java.util.stream.Collectors;

class Student {
    String name;
    int    marks;

    Student(String name, int marks) {
        this.name  = name;
        this.marks = marks;
    }

    @Override
    public String toString() {
        return name + "(" + marks + ")";
    }
}

public class CustomObjectList {
    public static void main(String[] args) {

        List<Student> students = new ArrayList<>();
        students.add(new Student("Arjun",  88));
        students.add(new Student("Priya",  95));
        students.add(new Student("Rohan",  72));
        students.add(new Student("Sneha",  89));
        students.add(new Student("Vikram", 65));

        System.out.println("All students: " + students);

        // Sort by marks descending
        students.sort(Comparator.comparingInt((Student s) -> s.marks).reversed());
        System.out.println("By marks (desc): " + students);

        // Sort by name alphabetically
        students.sort(Comparator.comparing(s -> s.name));
        System.out.println("By name (alpha): " + students);

        // Filter students who scored >= 85 using Stream
        List<Student> toppers = students.stream()
            .filter(s -> s.marks >= 85)
            .collect(Collectors.toList());
        System.out.println("Toppers (>=85): " + toppers);

        // Find the highest scorer
        Student best = students.stream()
            .max(Comparator.comparingInt(s -> s.marks))
            .orElseThrow();
        System.out.println("Top scorer: " + best);
    }
}

Output

All students: [Arjun(88), Priya(95), Rohan(72), Sneha(89), Vikram(65)] By marks (desc): [Priya(95), Sneha(89), Arjun(88), Rohan(72), Vikram(65)] By name (alpha): [Arjun(88), Priya(95), Rohan(72), Sneha(89), Vikram(65)] Toppers (>=85): [Arjun(88), Priya(95), Sneha(89)] Top scorer: Priya(95)

Nested ArrayList — 2D List in Java

Java does not have a built-in 2D ArrayList, but you can easily create one using ArrayList<ArrayList<T>>. This is commonly used for matrix operations, adjacency lists in graphs, and storing variable-length rows.

☕ JavaNested ArrayList — 2D List
import java.util.*;

public class NestedArrayList {
    public static void main(String[] args) {

        // Create a 3x3 matrix using nested ArrayList
        List<List<Integer>> matrix = new ArrayList<>();

        for (int i = 0; i < 3; i++) {
            List<Integer> row = new ArrayList<>();
            for (int j = 0; j < 3; j++) {
                row.add((i + 1) * (j + 1));  // multiplication table
            }
            matrix.add(row);
        }

        // Print the matrix
        System.out.println("Multiplication Matrix:");
        for (List<Integer> row : matrix) {
            for (int val : row) {
                System.out.printf("%4d", val);
            }
            System.out.println();
        }

        // Access specific element — row 1, col 2 (0-indexed)
        System.out.println("\nElement at [1][2]: " + matrix.get(1).get(2));

        // Variable-length rows (jagged 2D list)
        List<List<String>> jagged = new ArrayList<>();
        jagged.add(new ArrayList<>(List.of("A")));
        jagged.add(new ArrayList<>(List.of("B", "C")));
        jagged.add(new ArrayList<>(List.of("D", "E", "F")));
        System.out.println("\nJagged List: " + jagged);
    }
}

Output

Multiplication Matrix: 1 2 3 2 4 6 3 6 9 Element at [1][2]: 6 Jagged List: [[A], [B, C], [D, E, F]]

Best Practices for Using ArrayList

Writing clean, efficient ArrayList code requires following these industry-standard best practices used in professional Java development:

  • ✅ 1. Program to the List Interface — Always declare ArrayList variables as List<T> not ArrayList<T>. For example: List<String> names = new ArrayList<>(). This decouples your code from the specific implementation, making it easy to swap to LinkedList or CopyOnWriteArrayList without changing callers.

  • ✅ 2. Specify Initial Capacity When Size Is Known — If you expect approximately N elements, construct with new ArrayList<>(N). This prevents multiple internal resize-and-copy cycles, improving performance for large lists. This is especially important in loops that add thousands of elements.

  • ✅ 3. Always Use Generics — Never Raw Types — List list = new ArrayList() is a raw type and bypasses compile-time type checking. Always use List<String> list = new ArrayList<>() to catch type mismatch bugs at compile time, not at runtime.

  • ✅ 4. Use Iterator or removeIf() for Deletion During Traversal — Never remove elements inside a for-each loop — it throws ConcurrentModificationException. Use Iterator.remove() for conditional removal during traversal, or the cleaner Java 8+ list.removeIf(predicate) for batch removal.

  • ✅ 5. Use Collections.unmodifiableList() for Read-Only Lists — When passing a list to external code that should not modify it, wrap it: Collections.unmodifiableList(myList). Any modification attempt throws UnsupportedOperationException immediately, preventing accidental mutation.

  • ✅ 6. Prefer List.of() for Fixed Immutable Lists — For a list of known constants that will never change, Java 9+ List.of("A", "B", "C") creates a compact, immutable list faster than constructing an ArrayList. Never use List.of() when you need to add or remove elements later.

  • ✅ 7. Use Stream API for Filter/Map/Collect Pipelines — For transformations (filtering, mapping, grouping), streams are more readable and often more performant than manual loops over ArrayList. Prefer list.stream().filter().map().collect() over imperative for-loops for such operations.

  • ❌ 8. Avoid Frequent Insertions at Index 0 — Inserting at the beginning of an ArrayList is O(n) because all existing elements must shift right. For frequent head insertions or removals, use LinkedList or ArrayDeque instead.

☕ JavaBest Practices — Code Demo
import java.util.*;
import java.util.stream.Collectors;

public class BestPracticesDemo {
    public static void main(String[] args) {

        // ✅ Program to interface
        List<String> names = new ArrayList<>(10);  // initial capacity hint
        names.addAll(List.of("Alice", "Bob", "Charlie", "David", "Eve"));

        // ✅ removeIf — cleaner than iterator loop
        names.removeIf(name -> name.startsWith("D"));
        System.out.println("After removeIf: " + names);

        // ✅ Stream pipeline — filter and collect
        List<String> longNames = names.stream()
            .filter(n -> n.length() > 3)
            .map(String::toUpperCase)
            .collect(Collectors.toList());
        System.out.println("Long names uppercase: " + longNames);

        // ✅ Immutable view — safe to pass externally
        List<String> readOnly = Collections.unmodifiableList(names);
        System.out.println("Read-only view: " + readOnly);
        // readOnly.add("Frank"); // ← UnsupportedOperationException

        // ✅ List.of() for known constants
        List<String> weekdays = List.of("Mon", "Tue", "Wed", "Thu", "Fri");
        System.out.println("Weekdays: " + weekdays);
    }
}

Output

After removeIf: [Alice, Bob, Charlie, Eve] Long names uppercase: [ALICE, CHARLIE] Read-only view: [Alice, Bob, Charlie, Eve] Weekdays: [Mon, Tue, Wed, Thu, Fri]

Real-World Code Examples

Example 1 — Shopping Cart Manager

☕ JavaShopping Cart with ArrayList
import java.util.*;
import java.util.stream.Collectors;

class Product {
    String name;
    double price;
    int    quantity;

    Product(String name, double price, int quantity) {
        this.name     = name;
        this.price    = price;
        this.quantity = quantity;
    }

    double total() { return price * quantity; }

    @Override
    public String toString() {
        return String.format("%s x%d @₹%.0f", name, quantity, price);
    }
}

public class ShoppingCart {

    private List<Product> cart = new ArrayList<>();

    public void addItem(Product p)    { cart.add(p); }
    public void removeItem(String name) {
        cart.removeIf(p -> p.name.equalsIgnoreCase(name));
    }

    public double getTotal() {
        return cart.stream().mapToDouble(Product::total).sum();
    }

    public void printCart() {
        System.out.println("--- Cart ---");
        cart.forEach(p -> System.out.println("  " + p + " = ₹" + p.total()));
        System.out.printf("  Total: ₹%.2f%n", getTotal());
    }

    public static void main(String[] args) {
        ShoppingCart sc = new ShoppingCart();
        sc.addItem(new Product("Rice",   55.0, 2));
        sc.addItem(new Product("Dal",    120.0, 1));
        sc.addItem(new Product("Oil",    180.0, 1));
        sc.addItem(new Product("Biscuit", 30.0, 3));
        sc.printCart();

        sc.removeItem("Dal");
        System.out.println("\nAfter removing Dal:");
        sc.printCart();
    }
}

Output

--- Cart --- Rice x2 @₹55 = 110.0 Dal x1 @₹120 = 120.0 Oil x1 @₹180 = 180.0 Biscuit x3 @₹30 = 90.0 Total: ₹500.00 After removing Dal: --- Cart --- Rice x2 @₹55 = 110.0 Oil x1 @₹180 = 180.0 Biscuit x3 @₹30 = 90.0 Total: ₹380.00

Example 2 — Word Frequency Counter

☕ JavaWord Frequency with ArrayList + Map
import java.util.*;
import java.util.stream.Collectors;

public class WordFrequency {
    public static void main(String[] args) {

        String text = "java is great java is fast python is popular java rocks";
        List<String> words = new ArrayList<>(Arrays.asList(text.split(" ")));

        System.out.println("Total words: " + words.size());
        System.out.println("Unique words: " + new HashSet<>(words).size());

        // Count frequency using stream groupingBy
        Map<String, Long> freq = words.stream()
            .collect(Collectors.groupingBy(w -> w, Collectors.counting()));

        // Sort by frequency descending
        freq.entrySet().stream()
            .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
            .forEach(e -> System.out.println("  " + e.getKey() + " : " + e.getValue()));

        // Get distinct words sorted alphabetically
        List<String> distinct = words.stream()
            .distinct()
            .sorted()
            .collect(Collectors.toList());
        System.out.println("Distinct (sorted): " + distinct);
    }
}

Output

Total words: 10 Unique words: 5 java : 3 is : 3 great : 1 fast : 1 python : 1 popular : 1 rocks : 1 Distinct (sorted): [fast, great, is, java, popular, python, rocks]

Practice This Code — Live Editor

Advantages and Disadvantages of ArrayList

ArrayList is the most versatile and widely used collection in Java — but it is not the right tool for every job. Understanding its trade-offs helps you choose correctly.

✅ Advantages
O(1) Random AccessBacked by an array, get(index) and set(index) are O(1) — as fast as a native array. Perfect for index-heavy workloads like lookup tables and paginated results.
Dynamic SizingNo need to declare size upfront. ArrayList handles all internal resizing transparently, eliminating ArrayIndexOutOfBoundsException from size mismatch.
Full Collections Framework IntegrationImplements List completely and works with all Collections utility methods — sort, binarySearch, shuffle, reverse, frequency, disjoint — and all Java APIs that accept List.
Generics Type SafetyArrayList<T> catches type errors at compile time. You cannot accidentally store a wrong type, preventing ClassCastException at runtime.
Versatile IterationSupports six distinct traversal strategies — for loop, for-each, Iterator, ListIterator, forEach lambda, and Stream API — covering every traversal pattern cleanly.
Rich Java 8+ APIremoveIf(), replaceAll(), sort(), stream(), and forEach() turn ArrayList into a powerful functional collection with minimal boilerplate for filtering, mapping, and transforming data.
Memory EfficientStores only element references in a contiguous array. No per-element overhead (unlike LinkedList's prev/next node pointers), giving compact memory layout and better CPU cache performance.
❌ Disadvantages
Not Thread-SafeArrayList is not synchronized. Concurrent access from multiple threads without external synchronization causes data corruption or ConcurrentModificationException. Use CopyOnWriteArrayList or Collections.synchronizedList() for multi-threaded scenarios.
O(n) Middle Insertions and DeletionsInserting or deleting at any index other than the end requires shifting all subsequent elements — O(n). For workloads with frequent middle insertions, LinkedList is faster.
O(n) Insertions at Index 0Prepending to the beginning shifts every element right — the most expensive write operation. If you frequently add to or remove from the head, use ArrayDeque or LinkedList instead.
Wasted Capacity After ResizingAfter each growth cycle, approximately 33% of allocated capacity may be empty. Use trimToSize() or a carefully chosen initial capacity to avoid memory waste in large lists.
No Primitive Storage — Autoboxing OverheadArrayList<Integer> stores Integer objects, not int primitives. Autoboxing and unboxing incur object allocation overhead per element. For large primitive collections, use int[] arrays or IntStream.
Fail-Fast IteratorArrayList's Iterator throws ConcurrentModificationException if the list is structurally modified during iteration by anything other than Iterator.remove(). This trips up beginners who delete inside for-each loops.

Java ArrayList — Interview Questions

These are the most frequently asked interview questions on Java ArrayList for Java developer, backend engineer, and SDET positions.

Practice Questions — Test Your Knowledge

Test your understanding of ArrayList with these practice questions. Attempt each before revealing the answer.

1. What is the capacity of an ArrayList after adding 16 elements to a default-constructed ArrayList?

Easy

2. What is the output? List<String> list = new ArrayList<>(List.of("A","B","C")); list.remove(1); System.out.println(list);

Easy

3. Why does this code throw ConcurrentModificationException and how do you fix it? for (String s : list) { if (s.equals("X")) list.remove(s); }

Medium

4. What is the difference in behavior: list.remove(2) vs list.remove(Integer.valueOf(2)) on an ArrayList<Integer>?

Medium

5. You have an ArrayList of 1 million elements. Describe the performance of: (a) get(500000), (b) add("X") at end, (c) add(0, "X") at beginning, (d) contains("X") — explain Big-O for each.

Hard

6. What is the output? List<Integer> a = new ArrayList<>(List.of(1,2,3,4,5)); List<Integer> b = a.subList(1,4); b.set(0, 99); System.out.println(a);

Hard

7. What happens when you call Collections.sort() on an ArrayList of custom objects that do not implement Comparable?

Medium

8. Can an ArrayList hold different types of objects simultaneously? Is it good practice?

Easy

Conclusion — Mastering Java ArrayList

ArrayList is the workhorse of Java development — the first data structure you reach for when you need an ordered, indexed, dynamically-sized collection. Its combination of O(1) random access, rich API, seamless generics support, and full Collections Framework integration makes it the correct default choice for the vast majority of Java list use cases.

Mastering ArrayList means knowing not just how to use it, but when not to: use LinkedList when you frequently insert at both ends, use CopyOnWriteArrayList when reads dominate in a multi-threaded context, use int[] when you need a large primitive array without boxing overhead, and use List.of() when immutability is a requirement.

ScenarioBest Choice
General-purpose ordered list, single-threaded✅ ArrayList — default choice
Frequent insertions/deletions at head or tail✅ LinkedList or ArrayDeque
Thread-safe list, general purpose✅ Collections.synchronizedList(new ArrayList<>())
Thread-safe list, read-heavy (few writes)✅ CopyOnWriteArrayList
Fixed list of known constants, immutable✅ List.of() — compact and immutable
Large primitive numeric data (no boxing)✅ int[], long[], or IntStream
Need O(1) lookup (no ordering required)✅ HashSet or HashMap
Sorted unique elements✅ TreeSet

Your next steps: explore LinkedList for pointer-based list operations, dive into the Stream API for functional list processing, study Java 21's Sequenced Collections interface that standardizes first/last element access, and learn Collections utility methods (sort, binarySearch, nCopies, frequency, disjoint) that supercharge your ArrayList operations.

Remember: Declare as List, instantiate as ArrayList, specify initial capacity when size is known, never modify during for-each, and always use generics. These five habits will keep your ArrayList code correct, performant, and production-ready. ☕

Frequently Asked Questions (FAQ)