Lesson 4.9: Traversing ArrayLists
Lesson 4.9: Traversing ArrayLists
What You'll Learn
- 4.9.A: Traverse the elements in an ArrayList.
- Write standard for loops and for-each loops over ArrayLists.
- Explain why removing elements during a standard for loop requires adjusting the index.
- Identify the off-by-one and skipped-element bugs that occur when modifying a list during traversal.
- Choose the correct loop type based on whether the list will be modified.
Key Vocabulary
| Term | Definition |
|---|---|
| ConcurrentModificationException | A runtime exception thrown when a list is structurally modified (elements added or removed) while a for-each loop is iterating over it. |
| index adjustment | Decrementing the loop counter after a removal so the next element (which shifted left) is not skipped. |
Standard For Loop Traversal
The standard for loop traverses an ArrayList using size() as the bound and get(i) to access each element:
ArrayList names = new ArrayList();
names.add("Alice");
names.add("Ben");
names.add("Carlos");
for (int i = 0; i < names.size(); i++) {
System.out.println(names.get(i));
}
⚠️ AP Exam Trap: size() not length
The loop bound must be names.size(), not names.length. length is the array field — it doesn't exist on ArrayList. Using it causes a compile error.
For-Each Loop Traversal
When you only need to read every element front to back, the for-each loop is cleaner:
for (String name : names) {
System.out.println(name);
}
Read it as: "for each String named name in names, do..." — identical to how for-each works on arrays.
⚠️ AP Exam Trap: Never Remove Inside a For-Each
Removing an element from an ArrayList while a for-each loop is iterating over it throws a ConcurrentModificationException at runtime. If you need to remove elements during traversal, you must use a standard for loop with an index adjustment.
Removing Elements During Traversal
This is the most heavily tested ArrayList traversal concept on the AP exam. When you remove an element at index i, everything after it shifts left — the next element is now at index i, not i+1. If the loop then increments i normally, that next element gets skipped.
The fix: decrement i after every removal.
// Remove all elements less than 5
ArrayList nums = new ArrayList();
nums.add(3);
nums.add(8);
nums.add(2);
nums.add(9);
nums.add(1);
for (int i = 0; i < nums.size(); i++) {
if (nums.get(i) < 5) {
nums.remove(i);
i--; // ← adjust for the left shift
}
}
// nums: [8, 9]
✅ Tracing the Index Adjustment
Without i--: start [3,8,2,9,1]. i=0: remove 3 → [8,2,9,1]. i increments to 1. But the element that shifted to index 0 (value 8) was already passed — it's fine here. Now at index 1 is 2. Remove 2 → [8,9,1]. i increments to 2. Index 2 is 1 — remove it → [8,9]. i=3, size=2, loop ends. Works here by accident.
The problem is when two consecutive qualifying elements appear: [3,2,9]. i=0: remove 3 → [2,9]. i increments to 1. Index 1 is 9 — skips 2 entirely! With i--: after removing 3, i goes back to 0, sees 2, removes it, result [9]. Correct.
Choosing the Right Loop
| Situation | Use This Loop |
|---|---|
| Read every element, no modification | for-each ✓ |
| Remove elements during traversal | standard for + i-- ✓ |
Replace elements (set) during traversal |
standard for ✓ |
| Need the index value itself | standard for ✓ |
| Traverse in reverse | standard for (decrement) ✓ |
Summary
- Standard for loop:
for (int i = 0; i < list.size(); i++)withlist.get(i). - For-each loop:
for (Type x : list)— cleaner but cannot modify the list structure. - Removing during a for loop: always add
i--after the removal to avoid skipping the element that shifted into position. - Never remove inside a for-each loop — throws
ConcurrentModificationException.
Practice Questions
ArrayListlist = new ArrayList (); list.add(10); list.add(20); list.add(30); int sum = 0; for (int x : list) { sum += x; } System.out.println(sum);
words?for (int i = 0; i < words.length; i++) {
System.out.println(words.get(i));
}
for (int i = 0; i <= words.size(); i++) {
System.out.println(words.get(i));
}
for (int i = 0; i < words.size(); i++) {
System.out.println(words.get(i));
}
for (int i = 1; i < words.size(); i++) {
System.out.println(words.get(i));
}
words.length — ArrayList has no length field, compile error. B uses <= — throws IndexOutOfBoundsException on the last iteration. D starts at 1 — skips index 0. C is the correct pattern.ConcurrentModificationException is thrown.ConcurrentModificationException at runtime. This is not a compile error — the code looks valid to the compiler.list contain?
ArrayListlist = new ArrayList (); list.add(1); list.add(2); list.add(3); list.add(4); list.add(5); for (int i = 0; i < list.size(); i++) { if (list.get(i) % 2 == 0) { list.remove(i); i--; } }
i-- correctly adjusting after each removal, no elements are skipped. 2 and 4 are removed, leaving [1,3,5].ArrayListlist = new ArrayList (); list.add(2); list.add(4); list.add(6); for (int i = 0; i < list.size(); i++) { if (list.get(i) % 2 == 0) { list.remove(i); } }
i--, elements shift left but the index still increments. i=0: remove 2 → [4,6]. i increments to 1. Index 1 is 6 — remove it → [4]. i increments to 2, size is 1, loop ends. 4 was skipped entirely because after removing 2, it shifted to index 0, but i jumped to 1.Mastery: Traversing ArrayLists
words?for (String w : words) {
if (w.length() > 3) {
words.remove(w);
}
}
for (int i = 0; i < words.size(); i++) {
if (words.get(i).length() > 3) {
words.remove(i);
}
}
for (int i = 0; i < words.size(); i++) {
if (words.get(i).length() > 3) {
words.remove(i);
i--;
}
}
for (int i = words.size(); i >= 0; i--) {
if (words.get(i).length() > 3) {
words.remove(i);
}
}
i-- — skips elements after each removal. D starts at words.size() which is out of bounds (valid indices are 0 to size-1). C is correct: removes and adjusts index each time.ArrayListlist = new ArrayList (); list.add(5); list.add(3); list.add(8); list.add(3); list.add(7); int count = 0; for (int x : list) { if (x == 3) count++; } System.out.println(count);
Student class has getGpa() returning a double. Which code correctly removes all students with GPA below 2.0 from roster?for (Student s : roster) {
if (s.getGpa() < 2.0) {
roster.remove(s);
}
}
for (int i = 1; i < roster.size(); i++) {
if (roster.get(i).getGpa() < 2.0) {
roster.remove(i);
i--;
}
}
for (int i = 0; i < roster.size(); i++) {
if (roster.get(i).getGpa() < 2.0) {
roster.remove(i);
}
}
for (int i = 0; i < roster.size(); i++) {
if (roster.get(i).getGpa() < 2.0) {
roster.remove(i);
i--;
}
}
ArrayListlist = new ArrayList (); list.add(1); list.add(1); list.add(2); list.add(1); for (int i = 0; i < list.size(); i++) { if (list.get(i) == 1) { list.remove(i); i--; } } System.out.println(list.size());
for (int x : list) {
x = x * 2;
}
for (Integer x : list) {
x = x * 2;
}
for (int i = 0; i < list.size(); i++) {
list.set(i, list.get(i) * 2);
}
for (int i = 0; i < list.size(); i++) {
list.add(i, list.get(i) * 2);
}
add which inserts new elements and grows the list infinitely. C uses set to replace each element in place — the correct approach.Get in Touch
Whether you're a student, parent, or teacher — I'd love to hear from you.
Just want free AP CS resources?
Enter your email below and check the subscribe box — no message needed. Students get daily practice questions and study tips. Teachers get curriculum resources and teaching strategies.
Message Sent!
Thanks for reaching out. I'll get back to you within 24 hours.
Prefer email? Reach me directly at [email protected]