Calling mutators on every object in a list.
When the list itself stays put but each object inside needs to change, the mutator-traversal pattern is the right tool.
An ArrayList of objects often needs bulk updates: apply a discount to every Product, increment the age of every Pet, mark every Assignment as graded. The list keeps its size and order — only the objects' internal state changes.
Because the cells of the ArrayList hold references, mutating an object through a loop variable does reach the real object. This is the key difference between mutating objects and trying to reassign loop variables.
On the AP exam, this pattern shows up in class FRQs that ask you to "update every X in the list" — and the cleanest implementation uses the enhanced-for loop combined with a mutator method call.
Loop variables are references to real objects.
Mutator calls travel through references; reassignments don't.
The template
for (SourceType item : list) {
item.someMutator(args); // changes the actual object
}
This works because item holds a reference to the same object stored in the list. Calling a mutator updates the object's fields; the list sees those updates immediately because it points to the same object.
Concrete example
Assume a Product class with applyDiscount(double percent) that reduces its price. Apply a 10% discount to every product:
public void discountAll(ArrayList<Product> products) {
for (Product p : products) {
p.applyDiscount(10);
}
}
After the loop, every Product's internal price field has been reduced — without touching the list structure at all.
What does NOT work
for (Product p : products) {
p = new Product("Free", 0); // changes only the local variable
}
Reassigning p changes the local copy of the reference; the list still points to the original object. This is the classic enhanced-for trap from the Data Collections unit.
Indexed loop when you need the position
for (int i = 0; i < products.size(); i++) {
products.get(i).applyDiscount(10);
}
Functionally equivalent. Use this form only when you actually need the index (e.g., for messages like "Product 3 was discounted").
Vocabulary for bulk-update problems.
Used in any FRQ involving collections of mutable objects.
- Mutator — a method that changes an object's state.
- Reference — the value stored in a loop variable when iterating over objects.
- In-place mutation — modifying an existing object rather than replacing it.
- Bulk update — applying the same change to every object in a collection.
- Side effect — a state change caused by a method call.
- Aliasing — multiple references pointing to the same object.
How mutator traversal is tested.
Enhanced-for confusion and missing mutators are the top traps.
MCQ patterns to expect:
- "Did the list actually change?" A mutator call through the loop variable yes; reassigning the loop variable no.
- "What is each object's state after the loop?" Apply the mutator to every element in turn.
- "Does this work for primitives too?" No — enhanced-for over an
ArrayList<Integer>cannot mutate the values, becauseIntegeris immutable.
FRQ tip: use the mutator method named exactly in the class spec. Don't try to write to fields directly even if you know they exist.
Increment age for every pet.
Verify the change reaches the underlying objects.
Setup. A Pet class has a private age field, accessor getAge(), and mutator haveBirthday() that adds 1 to age. Given ArrayList<Pet> pets, advance every pet by one year.
public void everyoneAgesUp(ArrayList<Pet> pets) {
for (Pet p : pets) {
p.haveBirthday();
}
}
Trace. If pets contains three pets with ages 2, 5, and 7, after the loop their ages are 3, 6, and 8.
Verification.
for (Pet p : pets) {
System.out.println(p.getAge()); // 3, 6, 8
}
Why this matters. The list pets still has size 3 in the same order — the structural integrity is preserved while every internal state changes. That's the whole point of in-place mutation.
What about ArrayList<Integer>? If ages were just an ArrayList<Integer>, you could not mutate the values with a similar pattern — Integer has no mutator. Instead you'd need ages.set(i, ages.get(i) + 1) in an indexed loop.
Errors that break bulk updates.
Most come from confusing object mutation with reference reassignment.
- Reassigning the loop variable.
p = new Pet(...)changes nothing in the list. - Trying to mutate an
IntegerorString. Both are immutable; the only way to "change" them is toseta new value. - Calling a non-mutating method.
p.getAge() + 1computes a value but doesn't store it. Use the mutator. - Direct field access.
p.age = p.age + 1fails when the field isprivate; always use the mutator. - Forgetting to call the mutator. Returning
p.getAge() + 1doesn't update the pet's state.
Mutating list elements at a glance.
When the loop variable can — and can't — change the underlying data.
Object Mutator Reference
AP Quick Reference| Action | Effect on list | AP Exam Tip |
|---|---|---|
p.mutator() |
Updates real object | Works with enhanced for. |
p = new Type(...) |
No effect | Only changes local reference. |
list.set(i, p) |
Replaces element | Different from in-place mutation. |
Mutating Integer |
Impossible | Use set with a new value. |
Mutating String |
Impossible | Strings are immutable. |
How to lock in mutator traversal.
Run small programs and verify the list reflects the change.
Use the three practice tools below this lesson in order:
- MCQ Practice — for each option, ask: "Is this calling a mutator on the object, or trying to reassign the loop variable?" Only the first changes state.
- Java Lab — write methods that bulk-update lists of
Pet,Product, orStudentobjects. Print the list before and after to verify the change. - FRQ Practice — use the mutator method named in the prompt. If the prompt only provides accessors, you may need to
seta new object in an indexed loop instead.
If you can answer "does this loop change the list?" instantly, the most common AP MCQ trap is solved.
Practice — attempt these now.
AP-style assessments aligned to this lesson. Time them.