Accumulate first, remove later.
Two-phase processing: build a result while reading; clean up the source after.
Many AP-style problems require both producing a result and modifying the source. The cleanest approach is the two-phase pattern: read through the source once to accumulate results, then remove items from the source in a separate, safe pass.
Mixing accumulation with removal in a single forward loop leads to the classic skip-elements bug. Separating the phases makes the code easier to reason about and easier for graders to follow.
On the AP exam, this pattern appears when an FRQ asks something like: "Return a list of overdue books, and remove them from the library."
Phase 1: read and collect. Phase 2: remove backwards.
Each phase has a clear, single responsibility.
The two-phase template
public ArrayList<SourceType> extractAndRemove(ArrayList<SourceType> source) {
ArrayList<SourceType> result = new ArrayList<SourceType>();
// Phase 1: accumulate matches (read-only on source)
for (SourceType item : source) {
if (/* condition */) {
result.add(item);
}
}
// Phase 2: remove matches from source (backwards iteration)
for (int i = source.size() - 1; i >= 0; i--) {
if (/* same condition */) {
source.remove(i);
}
}
return result;
}
Two distinct loops. Phase 1 is read-only on the source — enhanced-for is perfect. Phase 2 is destructive — indexed backwards loop is required.
Concrete example
Assume a Book class with isOverdue(). Extract overdue books into a returned list and remove them from the library:
public ArrayList<Book> checkOutOverdue(ArrayList<Book> library) {
ArrayList<Book> overdue = new ArrayList<Book>();
for (Book b : library) {
if (b.isOverdue()) {
overdue.add(b);
}
}
for (int i = library.size() - 1; i >= 0; i--) {
if (library.get(i).isOverdue()) {
library.remove(i);
}
}
return overdue;
}
Why two phases?
Mixing them in one forward loop creates the classic bug: each remove(i) shifts later elements left, but i keeps incrementing — so the element that took the removed slot gets skipped.
Two phases sidesteps the problem entirely. Phase 1 reads without modifying. Phase 2 removes safely with a backward index.
Shared references in the result
The returned list holds the same object references that the source originally held. After Phase 2, the source no longer contains those references — but the result list still does, so the objects survive.
Two-phase vocabulary.
Used in combined extract-and-clean problems.
- Two-phase processing — splitting accumulation and removal into separate passes.
- Phase 1 (accumulate) — building a result list without modifying the source.
- Phase 2 (remove) — deleting matching elements from the source.
- Destructive operation — one that changes the structure of the collection.
- Non-destructive operation — read-only on the source.
- Reference survival — objects live as long as some list still holds a reference.
How extract-and-remove is tested.
Skip bugs and missing removals dominate.
What FRQ graders check:
- Both phases present. Accumulation and removal are both required if the prompt mentions both.
- Backward iteration in Phase 2. Forward iteration with removal causes skips.
- Same condition in both phases. If the conditions diverge, you remove the wrong items.
- Return type. Usually
ArrayList<SourceType>— the accumulated matches.
MCQ patterns to expect: tracing the source's contents after the method runs; verifying the returned list has the right objects.
Pull failing students from a roster.
Return them; remove them from the source.
Setup. A Student class has getGpa(). Given ArrayList<Student> roster, return a new list of every student with GPA below 2.0, and remove them from the roster.
public ArrayList<Student> pullFailing(ArrayList<Student> roster) {
ArrayList<Student> failing = new ArrayList<Student>();
for (Student s : roster) {
if (s.getGpa() < 2.0) {
failing.add(s);
}
}
for (int i = roster.size() - 1; i >= 0; i--) {
if (roster.get(i).getGpa() < 2.0) {
roster.remove(i);
}
}
return failing;
}
Trace. Roster has five students with GPAs [3.5, 1.8, 2.7, 1.2, 3.9].
- Phase 1: collect students at indices 1 and 3 →
failinghas 2 students. - Phase 2 backwards: i=4 (3.9, keep); i=3 (1.2, remove); i=2 (2.7, keep); i=1 (1.8, remove); i=0 (3.5, keep).
Final. Roster: 3 students with GPAs [3.5, 2.7, 3.9]. Returned list: 2 students with GPAs [1.8, 1.2].
Why this matters. Notice that Phase 1 captured both failing students in order. Phase 2 removed them safely thanks to the backward index. Mixing the two in a single forward loop would have skipped one of the failing students after the first removal shifted indices.
Two-phase errors.
All come from skipping a phase or merging them carelessly.
- Forgetting Phase 2. You return the matches but leave them in the source — half the prompt unmet.
- Forgetting Phase 1. You remove correctly but return an empty list.
- Different conditions in each phase. Subtle drift between the two boolean expressions removes the wrong elements.
- Forward removal in Phase 2. Skips elements; results in some matches surviving in the source.
- Modifying source during Phase 1. Defeats the purpose of separating phases.
- Trying to swap the order. Removing first means Phase 1 sees a shrunken source — collection becomes inconsistent.
Two-phase template at a glance.
Each phase has its own job and its own loop type.
Accumulate + Remove Reference
AP Quick Reference| Phase | Job | Loop type |
|---|---|---|
| 1 — Accumulate | Add matches to result list | Enhanced for (read-only) |
| 2 — Remove | Delete matches from source | Indexed backward for |
| Order | Phase 1 first | Don't swap. |
| Conditions | Identical in both phases | Copy carefully. |
| Return value | The accumulator | Same element type as source. |
How to lock in two-phase processing.
Write both phases every time; check the source and result separately.
Use the three practice tools below this lesson in order:
- MCQ Practice — for each option, ask: "Were the matches added to the result?" and "Were they removed from the source?" Both must be true.
- Java Lab — write extract-and-remove methods for several specs: pull failing students, withdraw overdue books, archive completed tasks. Print both source and result to verify.
- FRQ Practice — read the prompt for two verbs: "return all X" (Phase 1) and "remove them" (Phase 2). Both phases must appear in your code.
If you can write both phases automatically when you read "extract and remove", this combined pattern becomes a confident, high-credit FRQ response.
Practice — attempt these now.
AP-style assessments aligned to this lesson. Time them.