ArrayList in Java – Complete Guide with Examples
ArrayList is the most used collection class in Java. It stores elements in order, like an array, but it grows and shrinks automatically as you add and remove elements. It lives in the java.util package, implements the List interface and has been part of the Collections Framework since JDK 1.2.
In this tutorial you will learn how to create an ArrayList, add, read, update and remove elements, loop through it, sort it, convert it to and from arrays, and understand how it works inside. Every example is a complete program you can copy and run.
1. What Is an ArrayList?
An ArrayList is a resizable array. A normal Java array has a fixed length: once you create new String[5], it can never hold a sixth element. An ArrayList removes that limit. You simply call add(), and it makes room for the new element by itself.
Think of it as a notebook with removable pages. You can add a page at the end, insert one in the middle, tear one out, or jump straight to page 7. The notebook keeps the pages in order and gets thicker or thinner as needed.
Key features of ArrayList:
- Ordered: elements keep the order in which you add them, and each one has an index starting at
0. - Dynamic size: the list grows automatically when it is full.
- Fast random access:
get(index)takes the same short time whatever the index, because the data is stored in an array. - Duplicates and
nullallowed: you can add the same value many times, andnullis a valid element. - Objects only: it stores objects, so primitives such as
intare stored as wrapper objects likeInteger(Java converts them for you through autoboxing). - Not synchronized: it is not thread-safe. If several threads change the same list, you must add synchronization yourself.
- Generic:
ArrayList<String>accepts only strings, so type errors are caught at compile time.
2. Class Hierarchy and Declaration
ArrayList extends AbstractList and implements the List, RandomAccess, Cloneable and Serializable interfaces. Because List extends Collection, which extends Iterable, an ArrayList can be used anywhere a List, Collection or Iterable is expected.
The class is declared like this in the JDK:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
Here E is the type parameter: the type of elements the list holds. RandomAccess is a marker interface that tells algorithms the list supports fast access by index.
3. Your First ArrayList Program
This program creates a list of cities, adds three elements, reads one by index and loops through all of them.
import java.util.ArrayList;
import java.util.List;
public class ArrayListQuickStart {
public static void main(String[] args) {
// Create an empty list that holds String values
List<String> cities = new ArrayList<>();
// Add elements (the list grows automatically)
cities.add("Delhi");
cities.add("Mumbai");
cities.add("Pune");
System.out.println("Cities: " + cities);
System.out.println("First city: " + cities.get(0));
System.out.println("Total cities: " + cities.size());
// Loop through every element
for (String city : cities) {
System.out.println("Visiting " + city);
}
}
}
Cities: [Delhi, Mumbai, Pune] First city: Delhi Total cities: 3 Visiting Delhi Visiting Mumbai Visiting Pune
List<String> and create it with new ArrayList<>(). Your code then depends on the interface, so you can switch to another list type later by changing one line. The empty <> (the diamond operator) lets Java infer the element type.4. Creating an ArrayList (Constructors)
ArrayList has three constructors:
| Constructor | What it does |
|---|---|
ArrayList() | Creates an empty list. Space for 10 elements is reserved when the first element is added. |
ArrayList(int initialCapacity) | Creates an empty list with room for the given number of elements. Use it when you know roughly how many elements you will add. |
ArrayList(Collection<? extends E> c) | Creates a list containing all elements of another collection, in that collection's iteration order. |
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
public class CreateArrayList {
public static void main(String[] args) {
// 1. Empty list with the default capacity
List<String> names = new ArrayList<>();
// 2. Empty list with room for 500 elements before it has to grow
List<Integer> orderIds = new ArrayList<>(500);
// 3. A list that starts with a copy of another collection
Set<String> skills = new TreeSet<>(Set.of("Java", "SQL", "Git"));
List<String> skillList = new ArrayList<>(skills);
// 4. A modifiable list filled with starting values
List<String> colors = new ArrayList<>(List.of("Red", "Green", "Blue"));
colors.add("Yellow");
System.out.println(names + " size=" + names.size());
System.out.println(orderIds + " size=" + orderIds.size());
System.out.println(skillList);
System.out.println(colors);
}
}
[] size=0 [] size=0 [Git, Java, SQL] [Red, Green, Blue, Yellow]
Notice that orderIds has a size of 0 even though it has a capacity of 500. Capacity is reserved space; size is the number of elements actually stored.
5. Adding Elements
Use add(element) to append to the end, add(index, element) to insert at a position, and addAll() to add every element of another collection. When you insert in the middle, the elements after that position shift one place to the right.
import java.util.ArrayList;
import java.util.List;
public class AddElements {
public static void main(String[] args) {
List<Integer> scores = new ArrayList<>();
scores.add(40); // add at the end
scores.add(60);
scores.add(80);
System.out.println("After add(): " + scores);
scores.add(1, 50); // insert at index 1, others shift right
System.out.println("After add(1, 50): " + scores);
List<Integer> bonus = List.of(90, 100);
scores.addAll(bonus); // add a whole collection at the end
System.out.println("After addAll(bonus): " + scores);
scores.addAll(0, List.of(10, 20)); // insert a collection at index 0
System.out.println("After addAll(0, [...]): " + scores);
}
}
After add(): [40, 60, 80] After add(1, 50): [40, 50, 60, 80] After addAll(bonus): [40, 50, 60, 80, 90, 100] After addAll(0, [...]): [10, 20, 40, 50, 60, 80, 90, 100]
6. Accessing and Searching Elements
get(index) reads an element. indexOf() and lastIndexOf() find the position of a value (or return -1 when it is missing), and contains() tells you whether a value is present. subList(from, to) returns a view of part of the list, where from is included and to is excluded.
import java.util.ArrayList;
import java.util.List;
public class AccessElements {
public static void main(String[] args) {
List<String> fruits = new ArrayList<>(
List.of("Apple", "Mango", "Banana", "Mango", "Grapes"));
System.out.println("get(2): " + fruits.get(2));
System.out.println("indexOf(Mango): " + fruits.indexOf("Mango"));
System.out.println("lastIndexOf(Mango): " + fruits.lastIndexOf("Mango"));
System.out.println("indexOf(Kiwi): " + fruits.indexOf("Kiwi"));
System.out.println("contains(Banana): " + fruits.contains("Banana"));
System.out.println("size(): " + fruits.size());
System.out.println("isEmpty(): " + fruits.isEmpty());
System.out.println("subList(1, 3): " + fruits.subList(1, 3));
try {
fruits.get(5); // valid indexes are 0 to 4
} catch (IndexOutOfBoundsException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
get(2): Banana indexOf(Mango): 1 lastIndexOf(Mango): 3 indexOf(Kiwi): -1 contains(Banana): true size(): 5 isEmpty(): false subList(1, 3): [Mango, Banana] Error: Index 5 out of bounds for length 5
0 to size() - 1. Any other index throws IndexOutOfBoundsException.7. Updating Elements
set(index, element) replaces the element at a position and returns the value it replaced. replaceAll() applies a function to every element.
import java.util.ArrayList;
import java.util.List;
public class UpdateElements {
public static void main(String[] args) {
List<String> tasks = new ArrayList<>(List.of("Plan", "Code", "Tset", "Deploy"));
System.out.println("Before: " + tasks);
// set() replaces the element and returns the old value
String old = tasks.set(2, "Test");
System.out.println("Replaced '" + old + "' with 'Test'");
System.out.println("After: " + tasks);
// replaceAll() changes every element using a function
tasks.replaceAll(String::toUpperCase);
System.out.println("Upper: " + tasks);
}
}
Before: [Plan, Code, Tset, Deploy] Replaced 'Tset' with 'Test' After: [Plan, Code, Test, Deploy] Upper: [PLAN, CODE, TEST, DEPLOY]
8. Removing Elements
You can remove by index, by value, all values found in another collection, all values that match a condition, or everything at once.
import java.util.ArrayList;
import java.util.List;
public class RemoveElements {
public static void main(String[] args) {
List<String> langs = new ArrayList<>(
List.of("Java", "Python", "Go", "Kotlin", "Rust", "JavaScript", "C"));
System.out.println("Start: " + langs);
langs.remove(2); // by index -> removes "Go"
System.out.println("remove(2): " + langs);
langs.remove("Rust"); // by value -> removes "Rust"
System.out.println("remove(\"Rust\"): " + langs);
langs.removeAll(List.of("C", "Python"));
System.out.println("removeAll(...): " + langs);
langs.removeIf(s -> s.startsWith("Java"));
System.out.println("removeIf(Java*): " + langs);
langs.clear();
System.out.println("clear(): " + langs + " isEmpty=" + langs.isEmpty());
// Careful with Integer lists: remove(int) vs remove(Object)
List<Integer> nums = new ArrayList<>(List.of(10, 20, 30));
nums.remove(1); // removes the element at index 1 (20)
nums.remove(Integer.valueOf(10)); // removes the value 10
System.out.println("Integer list: " + nums);
}
}
Start: [Java, Python, Go, Kotlin, Rust, JavaScript, C]
remove(2): [Java, Python, Kotlin, Rust, JavaScript, C]
remove("Rust"): [Java, Python, Kotlin, JavaScript, C]
removeAll(...): [Java, Kotlin, JavaScript]
removeIf(Java*): [Kotlin]
clear(): [] isEmpty=true
Integer list: [30]
List<Integer>: remove(1) calls remove(int index) and removes the element at index 1. To remove the value 1, write remove(Integer.valueOf(1)).9. Iterating Over an ArrayList
There are five common ways to walk through an ArrayList. Choose the simplest one that does what you need.
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class IterateArrayList {
public static void main(String[] args) {
List<String> team = new ArrayList<>(List.of("Asha", "Ravi", "Meera"));
// 1. Classic for loop (you need the index)
for (int i = 0; i < team.size(); i++) {
System.out.println(i + ": " + team.get(i));
}
// 2. Enhanced for loop (simplest way to read every element)
for (String name : team) {
System.out.print(name + " ");
}
System.out.println();
// 3. forEach with a lambda
team.forEach(name -> System.out.print(name.toUpperCase() + " "));
System.out.println();
// 4. Iterator (the safe way to remove while looping)
Iterator<String> it = team.iterator();
while (it.hasNext()) {
if (it.next().startsWith("R")) {
it.remove();
}
}
System.out.println("After Iterator.remove(): " + team);
// 5. ListIterator (can go backwards and replace elements)
ListIterator<String> li = team.listIterator(team.size());
while (li.hasPrevious()) {
String name = li.previous();
li.set(name + "!");
}
System.out.println("After ListIterator.set(): " + team);
}
}
0: Asha 1: Ravi 2: Meera Asha Ravi Meera ASHA RAVI MEERA After Iterator.remove(): [Asha, Meera] After ListIterator.set(): [Asha!, Meera!]
| Way to loop | Use it when |
|---|---|
Classic for loop | You need the index of each element. |
Enhanced for loop | You only read the elements. This is the most readable choice. |
forEach(lambda) | You want a short one-line action for each element. |
Iterator | You need to remove elements while looping. |
ListIterator | You need to move backwards, or replace or insert elements while looping. |
10. Avoiding ConcurrentModificationException
ArrayList's iterator is fail-fast. If the list is changed structurally (an element added or removed) while an enhanced for loop is running, the next step of the loop throws ConcurrentModificationException. This happens even with a single thread.
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.List;
public class ConcurrentModification {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));
try {
for (Integer n : numbers) {
if (n % 2 == 0) {
numbers.remove(n); // WRONG: changes the list during the loop
}
}
} catch (ConcurrentModificationException e) {
System.out.println("Caught: " + e.getClass().getSimpleName());
}
// RIGHT: let the list remove the matching elements for you
List<Integer> fresh = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));
fresh.removeIf(n -> n % 2 == 0);
System.out.println("Odd numbers: " + fresh);
}
}
Caught: ConcurrentModificationException Odd numbers: [1, 3, 5]
To remove while looping, use removeIf() or an explicit Iterator and its remove() method, as shown in the previous section.
11. Sorting an ArrayList
Use Collections.sort(list) or list.sort(comparator). Passing a Comparator lets you sort in reverse order, by length, or by any field of your own objects.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class SortArrayList {
record Employee(String name, int salary) { }
public static void main(String[] args) {
List<String> words = new ArrayList<>(List.of("pear", "fig", "apple", "kiwi"));
Collections.sort(words); // natural (A to Z) order
System.out.println("A-Z: " + words);
words.sort(Comparator.reverseOrder()); // Z to A
System.out.println("Z-A: " + words);
words.sort(Comparator.comparing(String::length)); // shortest first
System.out.println("By size: " + words);
List<Employee> staff = new ArrayList<>(List.of(
new Employee("Neha", 72000),
new Employee("Arjun", 55000),
new Employee("Kabir", 90000)));
staff.sort(Comparator.comparingInt(Employee::salary).reversed());
staff.forEach(e -> System.out.println(e.name() + " -> " + e.salary()));
}
}
A-Z: [apple, fig, kiwi, pear] Z-A: [pear, kiwi, fig, apple] By size: [fig, pear, kiwi, apple] Kabir -> 90000 Neha -> 72000 Arjun -> 55000
The sort is stable: elements that compare as equal keep their original order. That is why pear stays before kiwi when sorting by length. The example uses a Java 16+ record for Employee; a normal class with getters works the same way.
12. Converting Between ArrayList and Array
You will often need to move data between arrays and lists, for example when an older API expects an array.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ConvertArrayList {
public static void main(String[] args) {
// Array -> ArrayList (modifiable copy)
String[] planets = {"Mercury", "Venus", "Earth"};
List<String> planetList = new ArrayList<>(Arrays.asList(planets));
planetList.add("Mars");
System.out.println("List: " + planetList);
// ArrayList -> Array
String[] back = planetList.toArray(new String[0]);
System.out.println("Array: " + Arrays.toString(back) + " length=" + back.length);
// Primitive int[] -> ArrayList<Integer> (needs boxing)
int[] marks = {70, 85, 92};
List<Integer> markList = new ArrayList<>(Arrays.stream(marks).boxed().toList());
System.out.println("Marks: " + markList);
// ArrayList<Integer> -> int[]
int[] again = markList.stream().mapToInt(Integer::intValue).toArray();
System.out.println("int[]: " + Arrays.toString(again));
// Arrays.asList() is fixed-size: add() fails
List<String> fixed = Arrays.asList("A", "B");
try {
fixed.add("C");
} catch (UnsupportedOperationException e) {
System.out.println("Arrays.asList() is fixed-size: " + e.getClass().getSimpleName());
}
}
}
List: [Mercury, Venus, Earth, Mars] Array: [Mercury, Venus, Earth, Mars] length=4 Marks: [70, 85, 92] int[]: [70, 85, 92] Arrays.asList() is fixed-size: UnsupportedOperationException
Arrays.asList() returns a fixed-size list backed by the array, and List.of() returns an unmodifiable list. Wrap either one in new ArrayList<>(...) when you need to add or remove elements.13. Removing Duplicates
Because ArrayList allows duplicates, removing them is a common task. A LinkedHashSet keeps only unique values and remembers the order in which they first appeared. The Stream API's distinct() gives the same result.
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
public class RemoveDuplicates {
public static void main(String[] args) {
List<String> visitors = new ArrayList<>(
List.of("Riya", "Sam", "Riya", "John", "Sam", "Anil"));
// LinkedHashSet removes duplicates and keeps the first-seen order
List<String> unique = new ArrayList<>(new LinkedHashSet<>(visitors));
System.out.println("Unique (Set): " + unique);
// Same result with streams
List<String> unique2 = visitors.stream().distinct().toList();
System.out.println("Unique (Stream): " + unique2);
}
}
Unique (Set): [Riya, Sam, John, Anil] Unique (Stream): [Riya, Sam, John, Anil]
14. How ArrayList Works Internally
Inside every ArrayList is a plain Java array called elementData, plus a size counter. Understanding these two fields explains almost everything about ArrayList's speed.
- Creation:
new ArrayList<>()starts with a shared empty array, so it uses almost no memory until you add something. - First add: an internal array with a capacity of 10 is created.
- Growing: when the array is full, a new array about 1.5 times bigger is created (
newCapacity = oldCapacity + oldCapacity / 2), and all elements are copied into it. Capacity grows 10 → 15 → 22 → 33 → 49 and so on. - Inserting or removing in the middle: elements after that position are shifted with
System.arraycopy(). - Shrinking: removing elements never shrinks the internal array automatically. Call
trimToSize()if you want to release the unused space.
Copying the array is expensive, but it happens rarely. Averaged over many calls, adding to the end costs constant time, which is called amortized O(1). If you know you will add many elements, create the list with a matching initial capacity, or call ensureCapacity(), to avoid repeated copying.
15. Time Complexity of Operations
| Operation | Time | Why |
|---|---|---|
get(i), set(i, e) | O(1) | Direct array index. |
add(e) at the end | O(1) amortized | Usually just writes to the next free slot; occasionally resizes. |
add(i, e), remove(i) | O(n) | Elements after i must shift. |
remove(size() - 1) | O(1) | Nothing to shift at the end. |
contains(e), indexOf(e), remove(Object) | O(n) | Checks elements one by one with equals(). |
size(), isEmpty() | O(1) | Reads the stored size field. |
If your program calls contains() very often on a large list, a HashSet (O(1) lookups) is usually a better choice.
16. ArrayList and Thread Safety
ArrayList is not synchronized. When two threads add to the same list at the same time, updates can be lost or an exception can be thrown. Use one of these thread-safe options instead:
Collections.synchronizedList(new ArrayList<>()): wraps every method in a lock. You must still synchronize manually while iterating.CopyOnWriteArrayList: copies the array on every write. Reads are fast and never blocked, so it is ideal when reads far outnumber writes, such as lists of listeners.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class ThreadSafeList {
public static void main(String[] args) throws InterruptedException {
List<Integer> plain = new ArrayList<>();
List<Integer> synced = Collections.synchronizedList(new ArrayList<>());
List<Integer> cow = new CopyOnWriteArrayList<>();
Runnable task = () -> {
for (int i = 0; i < 10_000; i++) {
synced.add(i);
cow.add(i);
try {
plain.add(i); // not thread-safe
} catch (ArrayIndexOutOfBoundsException ignored) {
// can happen when two threads resize the array at the same time
}
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println("synchronizedList size: " + synced.size());
System.out.println("CopyOnWriteArrayList size: " + cow.size());
System.out.println("plain ArrayList size: " + plain.size() + " (expected 20000)");
}
}
synchronizedList size: 20000 CopyOnWriteArrayList size: 20000 plain ArrayList size: 19982 (expected 20000)
17. Common ArrayList Methods
| Method | Description |
|---|---|
add(E e) | Adds an element at the end. Returns true. |
add(int index, E e) | Inserts an element at the given position. |
addAll(Collection c) | Adds all elements of a collection at the end. |
addAll(int index, Collection c) | Inserts all elements of a collection at the given position. |
get(int index) | Returns the element at the given position. |
set(int index, E e) | Replaces the element at the given position and returns the old one. |
remove(int index) | Removes the element at the given position and returns it. |
remove(Object o) | Removes the first occurrence of the value. Returns true if found. |
removeAll(Collection c) | Removes every element that is also in the given collection. |
retainAll(Collection c) | Keeps only the elements that are also in the given collection. |
removeIf(Predicate p) | Removes every element that matches the condition. |
clear() | Removes all elements. |
size() | Returns the number of elements. |
isEmpty() | Returns true if the list has no elements. |
contains(Object o) | Returns true if the value is in the list. |
indexOf(Object o) | Index of the first occurrence, or -1. |
lastIndexOf(Object o) | Index of the last occurrence, or -1. |
subList(int from, int to) | Returns a view of the elements from from (included) to to (excluded). |
sort(Comparator c) | Sorts the list using the comparator (null means natural order). |
replaceAll(UnaryOperator op) | Replaces each element with the result of the function. |
forEach(Consumer action) | Runs the action for every element. |
iterator(), listIterator() | Return iterators for looping, removing and (with ListIterator) moving backwards. |
toArray(), toArray(T[] a) | Copy the elements into a new array. |
ensureCapacity(int min) | Grows the internal array in advance to hold at least min elements. |
trimToSize() | Shrinks the internal array to the current size. |
clone() | Returns a shallow copy: a new list that holds the same element objects. |
18. ArrayList vs Array vs LinkedList vs Vector
| Feature | Array | ArrayList | LinkedList | Vector |
|---|---|---|---|---|
| Size | Fixed | Dynamic | Dynamic | Dynamic |
| Primitives | Yes (int[]) | No (wrappers) | No (wrappers) | No (wrappers) |
| Internal storage | Contiguous block | Resizable array | Doubly linked nodes | Resizable array |
| Access by index | O(1) | O(1) | O(n) | O(1) |
| Insert or remove at the start | Not possible | O(n) | O(1) | O(n) |
| Growth when full | Cannot grow | About 50% | One node at a time | 100% (doubles) |
| Thread-safe | No | No | No | Yes (synchronized) |
| Introduced in | JDK 1.0 | JDK 1.2 | JDK 1.2 | JDK 1.0 (legacy) |
Rule of thumb: use ArrayList by default. Use an array for fixed-size data or primitives where memory matters. Choose ArrayDeque (or LinkedList) when you mostly add and remove at both ends. Avoid Vector in new code; use a synchronized list or CopyOnWriteArrayList instead.
19. Best Practices
- Program to the interface: write
List<Order> orders = new ArrayList<>();, notArrayList<Order> orders. - Always use generics: raw types such as
new ArrayList()lose type safety and causeClassCastExceptionat runtime. - Set an initial capacity when you know the approximate size, to avoid repeated resizing.
- Use
isEmpty()instead ofsize() == 0; it reads more clearly. - Return an empty list, not
null: callers can loop over it without null checks. - Override
equals()andhashCode()in your own classes, otherwisecontains(),indexOf()andremove(Object)will not find equal objects. - Return unmodifiable views (
List.copyOf()orCollections.unmodifiableList()) from methods when callers should not change your internal list.
20. Common Mistakes
| Mistake | What happens | Fix |
|---|---|---|
Removing inside an enhanced for loop | ConcurrentModificationException | Use removeIf() or Iterator.remove(). |
list.remove(5) on a List<Integer> | Removes index 5, not the value 5 | list.remove(Integer.valueOf(5)) |
Calling add() on Arrays.asList() or List.of() | UnsupportedOperationException | Wrap it: new ArrayList<>(List.of(...)) |
Using get(size()) | IndexOutOfBoundsException | The last index is size() - 1. |
| Sharing one ArrayList between threads | Lost updates or exceptions | Use a synchronized list or CopyOnWriteArrayList. |
Calling contains() in a loop over a big list | O(n²) slowness | Copy the values into a HashSet first. |
21. Summary
- ArrayList is a resizable, ordered, index-based list in
java.utilthat allows duplicates andnull. - It is backed by an array: reading by index is O(1), adding at the end is amortized O(1), and inserting or removing in the middle is O(n).
- The default capacity is 10, and the list grows by about 50% when full.
- Use
removeIf()or anIteratorto remove elements while looping. - It is not thread-safe; use
Collections.synchronizedList()orCopyOnWriteArrayListfor shared lists. - Prefer it over arrays when the size changes, and over LinkedList and Vector in almost all cases.
Related Tutorials
- List Interface in Java
- LinkedList in Java
- Vector in Java
- Difference between ArrayList and LinkedList
- Difference between ArrayList and Vector
- Difference between ArrayList and Array
- Difference between List and Set
Reference: Java SE 21 API: java.util.ArrayList
Frequently Asked Questions (FAQ)
Q1. What is an ArrayList in Java?
An ArrayList is a class in java.util that works like an array whose size changes automatically. It keeps elements in insertion order, gives each element an index, allows duplicates and null, and implements the List interface.
List<String> names = new ArrayList<>();
names.add("Asha");
names.add("Ravi");
System.out.println(names.get(1)); // Ravi
Q2. What is the default capacity of an ArrayList?
The default capacity is 10. Since Java 8, new ArrayList<>() does not create that array immediately. It starts with a shared empty array and creates the 10-slot array when the first element is added, which saves memory for lists that stay empty.
Q3. How does an ArrayList grow when it is full?
It creates a new, larger array and copies all existing elements into it. The new capacity is about 1.5 times the old one (old + old / 2), so the capacity grows 10 → 15 → 22 → 33 → 49. The old array is then left for the garbage collector.
Q4. What is the difference between size and capacity?
- Size is the number of elements stored. You read it with
size(). - Capacity is the length of the internal array, the number of elements the list can hold before it must grow. There is no public method to read it.
Capacity is always greater than or equal to size. new ArrayList<>(100) has a capacity of 100 and a size of 0.
Q5. Can an ArrayList store primitive types like int?
No. Generics work only with objects, so you write ArrayList<Integer>, not ArrayList<int>. Java converts between int and Integer automatically (autoboxing and unboxing), so list.add(5) still works. For very large amounts of numbers, a plain int[] uses much less memory.
Q6. Does ArrayList allow duplicate and null values?
Yes to both. You can add the same value any number of times, and you can add null one or more times. If you need unique values, use a Set such as HashSet or LinkedHashSet.
Q7. Is ArrayList thread-safe? How can I make it thread-safe?
No, ArrayList is not synchronized. For shared lists you have two common options:
// Option 1: every method is synchronized
List<String> synced = Collections.synchronizedList(new ArrayList<>());
synchronized (synced) { // lock while iterating
for (String s : synced) { System.out.println(s); }
}
// Option 2: best when reads are much more frequent than writes
List<String> cow = new CopyOnWriteArrayList<>();
Q8. ArrayList vs LinkedList: which one should I use?
Use ArrayList in almost every case. It reads by index in O(1), uses less memory per element, and works well with the CPU cache. LinkedList is only better when you constantly insert or remove at the beginning of the list or through an iterator. Even then, ArrayDeque is usually faster for queue and stack operations.
Q9. What is the difference between ArrayList and Vector?
| ArrayList | Vector |
|---|---|
| Not synchronized, so faster | Every method synchronized, so slower |
| Grows by about 50% | Doubles its size by default |
| Iterator and ListIterator | Also supports the old Enumeration |
| Added in JDK 1.2 | Legacy class from JDK 1.0 |
Vector is rarely used in new code.
Q10. What is the difference between an ArrayList and an array?
- An array has a fixed length; an ArrayList grows and shrinks.
- An array can hold primitives; an ArrayList holds only objects.
- An array uses the
lengthfield; an ArrayList uses thesize()method. - An ArrayList offers ready-made methods such as
add(),remove(),contains()andsort(). - An array is slightly faster and uses less memory.
Both store their data in a contiguous array, so both give O(1) access by index.
Q11. Why does remove(1) on a List<Integer> remove the wrong element?
Because remove(1) matches remove(int index), so Java removes the element at index 1. To remove the value 1, pass an object:
List<Integer> nums = new ArrayList<>(List.of(5, 1, 9));
nums.remove(Integer.valueOf(1)); // removes the value 1
System.out.println(nums); // [5, 9]
Q12. What causes ConcurrentModificationException, and how do I avoid it?
It is thrown when a list is structurally changed while it is being iterated, other than through the iterator itself. The usual cause is calling list.remove() inside an enhanced for loop. Avoid it by using list.removeIf(...), Iterator.remove(), or by collecting the changes and applying them after the loop.
Q13. What is the difference between clear() and removeAll()?
clear() removes every element and is the fastest way to empty a list. removeAll(collection) removes only the elements that also appear in the given collection. Calling list.removeAll(list) empties the list too, but more slowly.
Q14. How do I remove duplicates from an ArrayList?
Copy it through a LinkedHashSet, which keeps the first occurrence of each value in its original order:
List<String> unique = new ArrayList<>(new LinkedHashSet<>(list));
// or, with streams
List<String> unique2 = list.stream().distinct().toList();
Q15. How do I sort an ArrayList of custom objects?
Pass a Comparator to sort(). You can chain comparators to sort by more than one field:
students.sort(Comparator.comparing(Student::getGrade)
.thenComparing(Student::getName));
Alternatively, make the class implement Comparable and call Collections.sort(students).
Q16. What is the difference between Arrays.asList(), List.of() and new ArrayList<>()?
Arrays.asList(...): fixed size. You can change elements withset(), but not add or remove. It is backed by the original array.List.of(...)(Java 9+): fully unmodifiable, and does not allownullelements.new ArrayList<>(...): fully modifiable.
Q17. Why should I declare the variable as List instead of ArrayList?
Writing List<String> items = new ArrayList<>(); means the rest of your code only depends on the List interface. If you later need a different implementation, such as CopyOnWriteArrayList, you change one line. It also makes method signatures more flexible, because callers can pass any kind of list.
Q18. How do I convert an ArrayList to an array?
Use toArray(T[]) to get an array of the right type:
String[] arr = list.toArray(new String[0]);
// Java 11+
String[] arr2 = list.toArray(String[]::new);
The no-argument toArray() returns an Object[], which you cannot cast to String[].
Q19. What do ensureCapacity() and trimToSize() do?
ensureCapacity(n) grows the internal array in advance so the next n elements can be added without resizing, which helps before a large batch of adds. trimToSize() shrinks the internal array to the current size, freeing unused memory after you finish building a large list. Both are methods of ArrayList, not of the List interface.
Q20. How do I check whether two ArrayLists are equal?
Use equals(). Two lists are equal when they have the same size and the same elements in the same order, compared with each element's equals():
List<Integer> a = new ArrayList<>(List.of(1, 2, 3));
List<Integer> b = List.of(1, 2, 3);
System.out.println(a.equals(b)); // true
System.out.println(a == b); // false (different objects)
To ignore order, compare sorted copies, or compare them as sets if duplicates do not matter.