SECTION 1
Historical Context & Motivation
Fixed-size arrays have been a cornerstone of programming since the earliest high-level languages, but their rigid length requirement poses a fundamental design problem: developers must predict collection sizes at compile time. When Java was released in 1995, its creators recognized that real-world applications—inventory systems, student rosters, game entities—frequently need collections that grow and shrink at runtime. The Java Collections Framework (JCF), introduced in Java 2, addressed this gap by providing a unified architecture of interfaces and implementations for dynamic data structures. At the center of the framework sits ArrayList, a resizable-array implementation of the List interface that combines the random-access speed of an array with the flexibility of automatic resizing.
1995 • Java 1.0 Released
1998 • Java Collections Framework (JCF)
2004 • Generics Added (Java 5)
2014 • Java 8 Streams & Lambdas
2020 • AP CSA Curriculum Emphasis
The core question ArrayList answers is deceptively simple: how do you maintain indexed, ordered access to elements while allowing the collection to change size on the fly? Understanding the methods that manipulate an ArrayList—adding, removing, accessing, and modifying elements—is essential for the AP exam and for writing robust Java programs in any professional context.
SECTION 2
Core Principles & Definitions
An ArrayList is a generic class in the java.util package that implements the List interface. It stores references to objects (not primitives) in a contiguous internal array and automatically resizes that backing array when capacity is exhausted. Because ArrayList uses zero-based indexing identical to standard arrays, transitioning between the two data structures is conceptually smooth. However, unlike arrays, ArrayList tracks its own size (the number of elements currently stored) independently of its internal capacity.
Dynamic Sizing
Object References Only
int must be wrapped as Integer via autoboxing.Zero-Based Indexing
size() − 1. Out-of-bounds access throws IndexOutOfBoundsException.Index Shifting
Generics & Type Safety
ArrayList<String> restricts the list to String objects, and the compiler enforces this constraint, preventing ClassCastException at runtime.SECTION 3
Visual Explanation — ArrayList in Memory
add(2, "X"), elements at indices 2–3 shift right. After remove(1), elements at indices 2–4 shift left and size decreases by one.The diagram above captures the two most exam-critical operations: indexed insertion and indexed removal. When you call add(2, "X"), every element from index 2 onward shifts one position to the right before the new element is placed at index 2. When you call remove(1), the element at index 1 is returned and every element from index 2 onward shifts one position to the left, closing the gap. Both operations mutate the size() accordingly, and the internal capacity remains unchanged unless a resize is triggered.
SECTION 4
How ArrayList Methods Work
AP-Tested ArrayList Methods
| Method Signature | Return Type | Behavior |
|---|---|---|
add(E obj) | boolean | Appends obj to the end; always returns true. |
add(int index, E obj) | void | Inserts obj at index; shifts subsequent elements right. |
get(int index) | E | Returns the element at index without modifying the list. |
set(int index, E obj) | E | Replaces the element at index with obj; returns the old element. |
remove(int index) | E | Removes and returns the element at index; shifts subsequent elements left. |
size() | int | Returns the number of elements currently in the list (not the capacity). |
The six ArrayList methods tested on the AP CSA exam
Notice that add(E obj) returns boolean while add(int index, E obj) returns void. Similarly, set and remove both return the displaced element, which is useful when you need to capture or inspect the value being replaced or deleted. The get method is a pure accessor—it never mutates the list. On the AP exam, questions frequently test whether students know which methods modify the list and which do not.
SECTION 5
Traversal & Modification Patterns
Traversing an ArrayList while simultaneously modifying it is one of the trickiest topics on the AP CSA exam. There are two primary traversal mechanisms: the standard indexed for loop and the enhanced for loop (for-each). The indexed loop grants full control over the index variable, making it safe to add or remove elements when you manage the index correctly. The enhanced for loop, by contrast, does not expose an index and will throw a ConcurrentModificationException if you modify the list during iteration.
i on a removal pass; the left-shift brings the next element to the current index. Alternatively, traverse backwards from size() − 1 to 0, which avoids this issue entirely.SECTION 6
Worked Example — Building and Querying a Roster
Consider the following scenario: a teacher maintains a student roster as an ArrayList<String>. The code below builds the roster, performs several mutations, and prints the final state. Trace each operation carefully.
Worked example
- 1
Step 1 — Initialize and Populate
Create the list and add four names:ArrayList<String> roster = new ArrayList<String>();roster.add("Alice"); roster.add("Bob"); roster.add("Carol"); roster.add("Dave");roster → ["Alice", "Bob", "Carol", "Dave"], size = 4 - 2
Step 2 — Indexed Insertion
roster.add(1, "Eve");Inserts "Eve" at index 1. "Bob", "Carol", and "Dave" each shift one position to the right.roster → ["Alice", "Eve", "Bob", "Carol", "Dave"], size = 5 - 3
Step 3 — Set (Replace)
String old = roster.set(3, "Carlos");Replaces the element at index 3 ("Carol") with "Carlos". The method returns the old value "Carol", stored inold.roster → ["Alice", "Eve", "Bob", "Carlos", "Dave"], old = "Carol" - 4
Step 4 — Remove by Index
roster.remove(0);Removes and returns "Alice". All remaining elements shift left.roster → ["Eve", "Bob", "Carlos", "Dave"], size = 4 - 5
Step 5 — Access and Size Check
System.out.println(roster.get(2) + " " + roster.size());roster.get(2)returns "Carlos" (index 2 after the previous removal).roster.size()returns 4.Output: Carlos 4
SECTION 7
ArrayList vs. Array — Strengths & Limitations
| Feature | Array | ArrayList |
|---|---|---|
| Size | Fixed at creation (.length) | Dynamic (.size()) |
| Primitives | Supports int, double, etc. directly | Requires wrapper classes (Integer, Double) |
| Access syntax | arr[i] | list.get(i) |
| Mutation syntax | arr[i] = val | list.set(i, val) |
| Insert / remove | Manual shifting required | Built-in methods with automatic shifting |
| Performance (random access) | O(1) | O(1) |
| Performance (insert/remove middle) | O(n) — manual | O(n) — automatic |
Key differences between arrays and ArrayLists in Java
Key takeaway
SECTION 8
Connection to Advanced Data Structures
| ArrayList (AP CSA) | Advanced Alternatives |
|---|---|
| Backed by a resizable array; O(n) insert/remove in the middle. | LinkedList — O(1) insert/remove at known position, but O(n) random access. |
| Not thread-safe; for single-threaded use. | CopyOnWriteArrayList — thread-safe variant that clones the backing array on each write. |
| Stores only objects; autoboxing adds overhead. | IntStream / primitive arrays — avoid boxing entirely for numeric workloads. |
| Ordered by insertion; no duplicate control. | HashSet / TreeSet — enforce uniqueness; TreeSet also maintains sorted order. |
ArrayList in the broader Collections landscape
In a college-level data structures course, you will formalize the performance characteristics of ArrayList in terms of amortized analysis. Although a single add that triggers a resize copies all n elements (O(n)), the doubling strategy ensures that the average cost per insertion is O(1) amortized. Understanding this tradeoff is foundational for algorithm design, and the intuition you build tracing ArrayList operations on the AP exam transfers directly to these more advanced analyses.
SECTION 9
Practice Problems
Question 1: CONCEPTUAL
list.add("X")
B) list.set(0, "Y")
C) list.get(2)
D) list.remove(1)- A.
list.add("X") - B.
list.set(0, "Y") - C.
list.get(2) - D.
list.remove(1)
Show answer (C)
get(int index) method is a pure accessor that returns the element at the given index without altering the list's contents or size. A is wrong because add appends an element, increasing the size. B is wrong because set replaces an existing element, mutating the list's contents. D is wrong because remove deletes an element and shifts subsequent elements left.Question 2: BASIC CALCULATION
ArrayList<String> items = new ArrayList<String>();
items.add("P");
items.add("Q");
items.add("R");
items.add(1, "S");
items.set(3, "T");
items.remove(0);
What does items contain after execution?
A) ["S", "Q", "T"]
B) ["Q", "S", "T"]
C) ["S", "Q", "R"]
D) ["P", "S", "T"]- A. ["S", "Q", "T"]
- B. ["Q", "S", "T"]
- C. ["S", "Q", "R"]
- D. ["P", "S", "T"]
Show answer (A)
add(1, "S") → ["P", "S", "Q", "R"]. set(3, "T") replaces index 3 → ["P", "S", "Q", "T"]. remove(0) removes "P" → ["S", "Q", "T"]. B is wrong because it reverses the order of S and Q. C is wrong because it ignores the set operation. D is wrong because it ignores the remove operation.Question 3: INTERMEDIATE
ArrayList<Integer> nums = new ArrayList<Integer>();
nums.add(5);
nums.add(10);
nums.add(15);
nums.add(20);
for (int i = nums.size() - 1; i >= 0; i--) {
if (nums.get(i) % 10 == 0) {
nums.remove(i);
}
}
System.out.println(nums);
A) [5, 15]
B) [5, 10, 15]
C) [5, 15, 20]
D) [5]- A. [5, 15]
- B. [5, 10, 15]
- C. [5, 15, 20]
- D. [5]
Show answer (A)
Question 4: APPLIED
removeDuplicates that takes an ArrayList<String> and removes all duplicate values so that only the first occurrence of each string remains. The order of remaining elements must be preserved. Do not use any data structures other than ArrayList.
Method signature:
public static void removeDuplicates(ArrayList<String> list)Show answer
public static void removeDuplicates(ArrayList<String> list) {
for (int i = 0; i < list.size(); i++) {
for (int j = i + 1; j < list.size(); j++) {
if (list.get(i).equals(list.get(j))) {
list.remove(j);
j--; // adjust index after removal
}
}
}
}
Rubric Breakdown:
• Point 1: Outer for loop from 0 to size() iterates through each element.
• Point 2: Inner for loop from i+1 to size() compares the current element with all elements that follow.
• Point 3: Calls list.remove(j) when a match is found, correctly removing the later duplicate.
• Point 4: Decrements j after removal (j--) so the element that shifted into position j is checked on the next iteration.
• Point 5: Because the outer loop advances normally and duplicates are always removed from later positions, the first occurrence is preserved and order is maintained.Question 5: CRITICAL THINKING
ArrayList<Integer>:
for (int i = 0; i < nums.size(); i++) {
if (nums.get(i) < 0) {
nums.remove(i);
}
}
(a) Explain the specific bug in this code by giving an input that produces incorrect output.
(b) Provide two distinct fixes and explain why each works.Show answer
nums = [-1, -2, 3], at i=0 the code removes -1, making the list [-2, 3]. The loop then increments i to 1 and checks index 1 (which is 3), completely skipping -2 that shifted into index 0. The result is [-2, 3] instead of the expected [3].
(b) Two Fixes:
• Fix 1 — Decrement after removal: Add i--; inside the if block after remove. This counteracts the loop's i++, keeping i at the same position so the element that shifted in is examined.
• Fix 2 — Backward traversal: Change the loop to for (int i = nums.size() - 1; i >= 0; i--). Removing an element only shifts elements at indices greater than i, all of which have already been processed, so no element is skipped.