Filter, then change — selective mutation.
Find the objects that qualify and update only them, leaving the others untouched.
Selective mutation is the natural pairing of two earlier patterns: a filter condition decides which objects qualify, and a mutator call updates each qualifying object's state. The list keeps every element; only the qualifiers change.
This pattern models real workflows. Apply a discount to expired-stock products. Mark only overdue tasks as red. Reset the score of every player who failed.
On the AP exam, this appears in FRQs that combine traversal with a conditional state change — and the cleanest implementation uses the enhanced-for loop with an if guard around the mutator call.
Read state, test condition, mutate if matched.
No new list — the same list is updated in place.
The template
for (SourceType item : list) {
if (/* filter condition on item */) {
item.someMutator(args);
}
}
Three pieces, in order: the loop, the guard, the mutator call. Items that fail the condition are simply skipped.
Concrete example
Assume a Product class with isExpired() and applyDiscount(double percent). Mark down every expired product by 50%:
public void discountExpired(ArrayList<Product> products) {
for (Product p : products) {
if (p.isExpired()) {
p.applyDiscount(50);
}
}
}
After the loop: every expired product has been discounted; every non-expired product is unchanged. The list still contains all original elements.
Why this is safe
We're not adding or removing anything — the list's size and order stay the same. Enhanced-for works perfectly because we're mutating through the loop variable's reference, not reassigning the loop variable.
When you need an indexed loop
Only when the mutation needs an index (rare). For example, if you need to set a different value at each position using the index itself:
for (int i = 0; i < products.size(); i++) {
if (products.get(i).isExpired()) {
products.get(i).setSku("OLD_" + i);
}
}
This does NOT remove
Selective mutation is different from selective removal. The list keeps every element; only some objects' internal state changes. If the prompt says "delete" or "remove", use a different pattern.
Vocabulary for selective change.
Used in any combined filter+mutate FRQ.
- Selective mutation — applying a mutator only to objects that match a condition.
- Guard — the
ifstatement that protects the mutator call. - In-place update — modifying existing objects rather than building a new list.
- Predicate — the boolean condition driving the filter.
- State preservation — keeping non-matching objects' fields unchanged.
- Structural preservation — keeping the list's size and order unchanged.
How selective mutation is tested.
Guards and mutator naming dominate.
What FRQ graders check:
- Correct guard. The filter condition matches the prompt's "if" or "when" clause.
- Correct mutator call. Use the method name and arguments exactly as specified.
- No structural change. Don't call
add,remove, orsetwhen the prompt only asks for mutation. - Return type. Usually
void— the change is the side effect.
MCQ patterns to expect: tracing object states after the loop; identifying which elements remained unchanged.
Penalize tardy assignments.
Find late items; deduct points from only those.
Setup. An Assignment class has isLate() returning boolean and deductPoints(int amt) reducing its grade. Given ArrayList<Assignment> submissions, deduct 10 points from every late submission.
public void penalizeLate(ArrayList<Assignment> submissions) {
for (Assignment a : submissions) {
if (a.isLate()) {
a.deductPoints(10);
}
}
}
Trace. Start with four submissions: late, on time, late, on time. After the loop, the two late submissions have 10 points deducted; the two on-time submissions are unchanged. The list still contains all four assignments in the same order.
Why this matters. The list structure is preserved entirely — only some objects' internal grades changed. This is exactly what selective mutation guarantees.
What if every submission were late? All would get the deduction. The pattern handles both extremes naturally.
Selective-mutation pitfalls.
Most come from doing too much or too little.
- Mutating every element. Forgetting the guard, so non-matching objects get changed too.
- Removing instead of mutating. The prompt said "discount", not "delete". Read carefully.
- Building a new list. If the prompt asks for an in-place change, don't return a different list.
- Reassigning the loop variable.
p = new Product(...)never changes the underlying list. - Wrong mutator name. Use the exact method name from the prompt; don't paraphrase.
- Inverted filter. Discounting non-expired products instead of expired ones.
Selective mutation at a glance.
What changes — and what doesn't.
Selective Mutation Reference
AP Quick Reference| Aspect | Affected? | AP Exam Tip |
|---|---|---|
| List size | No | Same number of elements. |
| List order | No | Objects stay at their original indices. |
| Matching objects' state | Yes | Mutator changes their fields. |
| Non-matching objects' state | No | Guard skips them. |
| Return type | Usually void |
Change is the side effect. |
| Loop type | Enhanced for | Indexed only if position is needed. |
How to master selective mutation.
Three pieces, in order: loop, guard, mutator.
Use the three practice tools below this lesson in order:
- MCQ Practice — for each problem, identify the guard condition and the mutator name before tracing. Most wrong answers fail on one of them.
- Java Lab — write methods like
penalizeLate,discountExpired,resetFailingPlayers. Verify non-matching objects are unchanged. - FRQ Practice — when the prompt says "for every X that meets Y, do Z", reach for this template. Use the exact mutator name from the spec.
If you can spot the three pieces in any prompt — predicate, mutator, list — selective mutation becomes routine.
Practice — attempt these now.
AP-style assessments aligned to this lesson. Time them.