AP COMPUTER SCIENCE A • DATA COLLECTIONS

ArrayList Traversals

Master the standard patterns for iterating through dynamic lists—essential for the AP exam and real-world Java development.

Historical Context & Motivation

Long before Java's ArrayList class existed, programmers relied on fixed-size arrays to store collections of data. While arrays offered fast index-based access, their rigid capacity created a persistent headache: any insertion or deletion required manual shifting of elements and tedious bookkeeping of the current logical size. The desire for a resizable, indexed collection with built-in traversal support drove the evolution of list data structures across multiple programming languages.

1972
C Arrays & Manual Loops
The C language formalized index-based array traversal with the classic for-loop idiom, establishing patterns still visible in Java today.
1998
Java Collections Framework
Java 2 (JDK 1.2) introduced the Collections Framework, including java.util.ArrayList, giving developers a resizable list backed by a growable internal array.
2004
Generics & Enhanced For
Java 5 added generics (ArrayList<E>) and the enhanced for-each loop, dramatically simplifying type-safe traversal code.
2014
Streams & Lambdas
Java 8 introduced the Stream API and lambda expressions, enabling functional-style traversals—though these remain outside the AP subset.

The central question this lesson addresses is straightforward yet nuanced: how do you visit every element in an ArrayList correctly, efficiently, and without introducing subtle bugs—especially when elements may be added or removed mid-traversal? Mastering these patterns is essential because the AP Computer Science A exam tests traversal fluency in both multiple-choice and free-response questions.

Core Principles & Definitions

An ArrayList traversal is the systematic process of visiting each element in the list exactly once (or, in some patterns, conditionally skipping elements). Understanding traversals requires a firm grasp of several foundational ideas that govern how Java's ArrayList behaves under iteration.

1

Zero-Based Indexing

ArrayList elements are indexed from 0 through size() − 1. An off-by-one error on the upper bound is the single most common traversal bug.
2

Dynamic Size

Unlike arrays, an ArrayList's size() can change during traversal when elements are added or removed, requiring special care in loop bounds.
3

Two Loop Idioms

The AP subset emphasizes two traversal patterns: the indexed for loop (full control over index) and the enhanced for-each loop (concise, read-only access).
4

ConcurrentModificationException

Modifying the ArrayList's structure (add/remove) during a for-each loop causes a runtime exception. Indexed loops with careful index adjustment are the safe alternative.
KEY TAKEAWAY
KEY TAKEAWAY

Visual Explanation

The diagram shows each iteration of an indexed for-loop over a five-element ArrayList. The variable i advances from 0 to 4, and the loop terminates when i reaches size() (5).

The diagram above illustrates the most fundamental ArrayList traversal: an indexed for loop. Notice that the guard condition uses the strict less-than operator (<) rather than <=. Using <= would attempt get(5) on a five-element list, triggering an IndexOutOfBoundsException. This off-by-one pitfall is one of the most commonly tested concepts on the AP exam, appearing in both standalone MCQ questions and as part of larger FRQ solutions.

How ArrayList Traversals Work

Pattern 1 — Indexed for Loop

The indexed for loop gives you explicit control over the loop variable. The canonical form is:

INDEXED FOR LOOP
for (int i = 0; i < list.size(); i++) { Type elem = list.get(i); // process elem }
i — current index; list.size() — evaluated each iteration (important when removing); get(i) — O(1) random access

Because list.size() is re-evaluated on every iteration, this pattern naturally adapts when you remove elements during traversal. However, after calling list.remove(i), the element that was at index i + 1 shifts down to index i. If you then increment i as usual, you skip that shifted element. The fix: decrement i after a removal, or traverse backward from size() − 1 down to 0.

Pattern 2 — Enhanced for-each Loop

FOR-EACH LOOP
for (Type elem : list) { // process elem (read-only) }
elem — a copy of the reference at each position; modifying the list's structure (add/remove) during this loop throws ConcurrentModificationException

The enhanced for-each loop is syntactic sugar over an Iterator. It is the preferred idiom when you need only read access to every element and do not need the index. It produces cleaner code and eliminates the risk of off-by-one index errors. The trade-off is that you cannot modify the list's structure (no add or remove calls) without triggering a runtime exception.

Pattern 3 — While Loop Traversal

WHILE LOOP
int i = 0; while (i < list.size()) { if (shouldRemove(list.get(i))) { list.remove(i); } else { i++; } }
Index increments only when no removal occurs, preventing the skip-after-remove bug.
AP Exam Tip

Common Traversal Patterns & Pitfalls

Beyond simple element access, ArrayList traversals appear in several recurring algorithmic patterns on the AP exam. Understanding these patterns will help you recognize which loop idiom to use and how to avoid the classic pitfalls that examiners love to exploit.

Left: a forward traversal with unconditional i++ skips the second "X" after removal. Right: a backward traversal naturally avoids this because the shifting only affects indices already visited.
Common AP exam traversal patterns and the recommended loop idiom for each.
PatternBest Loop IdiomKey Detail
Sum / Averagefor-eachAccumulator variable initialized before loop; divide by size() after
Search (find first match)Indexed for or whileReturn index or element immediately on match; return −1 / null after loop
Count occurrencesfor-eachNo modification needed; counter++ when condition met
Remove all matchesBackward for or whileNever use for-each; must handle index shift
Simultaneous comparison (adjacent pairs)Indexed for (i < size()−1)Compare get(i) with get(i+1); upper bound is size()−1 to avoid IOOBE

Worked Example

Consider the following problem, which mirrors a typical AP FRQ task: given an ArrayList<Integer> called scores containing [85, 42, 97, 38, 74, 55], remove all values below 50 and return the average of the remaining scores.

1
Step 1 — Choose the Right Loop PatternBecause we must remove elements during traversal, the for-each loop is off-limits. We select a backward indexed for loop to avoid the shift-and-skip bug.
2
Step 2 — Write the Removal Loopfor (int i = scores.size() - 1; i >= 0; i--) — inside the loop body: if (scores.get(i) < 50) { scores.remove(i); }
After loop: scores = [85, 97, 74, 55]
3
Step 3 — Trace the Removali=5: 55 ≥ 50 → keep. i=4: 74 ≥ 50 → keep. i=3: 38 < 50 → remove → [85, 42, 97, 74, 55]. i=2: 97 ≥ 50 → keep. i=1: 42 < 50 → remove → [85, 97, 74, 55]. i=0: 85 ≥ 50 → keep.
Remaining list: [85, 97, 74, 55]
4
Step 4 — Compute the Average with a for-each LoopNow that no structural modification is needed, a for-each loop is safe: int sum = 0; for (int s : scores) { sum += s; }. Then double avg = (double) sum / scores.size();
sum = 311; avg = 311.0 / 4 = 77.75
Why Not a Forward While Loop?

Comparing Traversal Approaches

Indexed for loop vs. enhanced for-each loop comparison
FeatureIndexed for LoopEnhanced for-each
Access to indexYes — i is availableNo — index hidden
Can add/remove elementsYes (with care)No — throws ConcurrentModificationException
Risk of off-by-one errorHigher — manual boundsNone — automatic bounds
Code concisenessModerateVery concise
Can traverse backwardYesNo
AP exam frequencyVery high (FRQs especially)High (MCQ and simple FRQs)
KEY TAKEAWAY
DECISION RULE

Connection to Advanced Concepts

ArrayList traversal mastery lays the groundwork for several topics you will encounter in data structures courses and professional development. The table below maps each AP-level concept to its more advanced counterpart, giving you a roadmap for future study.

AP ConceptAdvanced Extension
Indexed for loop over ArrayListIterator and ListIterator patterns; fail-fast vs. fail-safe iterators in concurrent collections
Remove-during-traversalIterator.remove() — the only safe structural modification during iteration in the general Collections API
For-each loopJava Streams (map, filter, reduce) — functional-style traversal with lazy evaluation
Linear search during traversalBinary search on sorted lists; hash-based O(1) lookups with HashMap

The Iterator design pattern (from the Gang of Four) is precisely what the enhanced for-each loop uses under the hood. When you write for (String s : list), the compiler translates it into an Iterator-based while loop. Understanding this connection will help you reason about why structural modifications cause a ConcurrentModificationException and why Iterator.remove() is the sanctioned escape hatch in post-AP Java programming.

Practice Problems

1
Which of the following is a valid reason to prefer an indexed for loop over an enhanced for-each loop when traversing an ArrayList?
2
Consider the following code: ArrayList<Integer> nums = new ArrayList<>(); nums.add(10); nums.add(20); nums.add(30); int total = 0; for (int n : nums) { total += n; } System.out.println(total); What is printed?
3
What does the following code print? ArrayList<String> words = new ArrayList<>(); words.add("A"); words.add("A"); words.add("B"); words.add("C"); for (int i = 0; i < words.size(); i++) { if (words.get(i).equals("A")) { words.remove(i); } } System.out.println(words);
PROBLEM 4APPLIED
Write a method removeDuplicates that takes an ArrayList<String> as a parameter and modifies it so that only the first occurrence of each string remains. The relative order of the remaining elements must be preserved. For example, if the list is ["a", "b", "a", "c", "b"], after the call it should be ["a", "b", "c"]. You may use an additional ArrayList for bookkeeping.
PROBLEM 5CRITICAL THINKING
A student writes the following code to compute the sum of all even integers in an ArrayList<Integer> called vals: int sum = 0; for (int i = 1; i <= vals.size(); i++) { if (vals.get(i) % 2 == 0) { sum += vals.get(i); } } (a) Identify two distinct bugs in this code. (b) Provide corrected code.
Varsity Tutors • AP Computer Science A • ArrayList Traversals