The loop runs without a syntax error, removes some matching words and leaves other matching words behind. The student looks at the condition and finds nothing wrong with it. The missing idea is that removing an ArrayList element changes the positions of the elements that follow.
For AP Computer Science A removal problems, keep track of the next unprocessed element after every change to the list. In a forward traversal, an element that shifts into the current index still needs examination. A controlled while loop can stay at that index after removal; a backward traversal can avoid disturbing the unprocessed lower indices.
This guide uses original Java examples and hand traces, with current College Board course and method documentation checked on 24 October 2026. It does not reproduce an official free-response question or predict assessment marks. The educational aim is understanding that transfers beyond a familiar example: explaining why every required element is considered when the input changes.
Find your next learning step
Trace how ArrayList removal changes indices, repair skipped adjacent elements and explain why a forward or backward traversal handles every item.
ROUTE 1 · CHAPTERS 1–3
Understand the moving collection
Match the course, locate the bug and track a removal.
ROUTE 2 · CHAPTERS 4–6
Repair the traversal
Trace the failure and explain forward and backward solutions.
ROUTE 3 · CHAPTERS 7–9
Make the algorithm dependable
State what is processed, handle boundaries and specify the method.
ROUTE 4 · CHAPTERS 10–12
Test and explain the behaviour
Use diagnostic inputs, separate operations and develop independent practice.
ROUTE 5 · CHAPTERS 13–15
Transfer the loop reasoning
Try a fresh task, resolve questions and consult primary documentation.
Full chapter index · Independent learning check · Education parent guide
Full chapter index
Understand the moving collection · Chapters 1–3
Repair the traversal · Chapters 4–6
Make the algorithm dependable · Chapters 7–9
Test and explain the behaviour · Chapters 10–12
Transfer the loop reasoning · Chapters 13–15
CHAPTER 1 OF 15 · Understand the moving collection
1. Match the lesson to AP Computer Science A
College Board's current AP Computer Science A Course and Exam Description is effective from fall 2025. Its Data Collections unit includes ArrayList methods, traversals and algorithms. The framework explicitly identifies the need for special techniques when deleting during traversal to avoid skipping elements. This is a reasoning problem within the course, not merely a formatting trick.
The examples here use ArrayList<String>, integer indices and ordinary selection and iteration. Assume that the lists themselves and their string elements are non-null. We will remove every string whose length is three while preserving the relative order of the retained strings. Those conditions define what success means for our exercises.
Keep the actual problem's specification in view. A task that asks you to remove only the first match, return a new list or change selected values has a different required behaviour. A correct solution to our all-matches, in-place task is not automatically a correct solution to every question containing remove.
The official Java Quick Reference is part of the current course documentation. Use its method behaviour to explain what changes. Broader Java APIs contain additional operations and language features, but they should not be imported as shortcuts without checking the course and question requirements. Our focus is the visible index and state reasoning.
Start by stating the intended result in ordinary language. After processing, no three-character strings should remain, every original nonmatching string should still be present, and their relative order should be unchanged. A learner who can articulate those conditions has a useful basis for checking the code.
CHAPTER 2 OF 15 · Understand the moving collection
2. Diagnose the missing state change
Imagine a fictional learner, Priya, who can explain a for loop on a fixed array but becomes uncertain when an ArrayList shrinks. Another learner, Ben, knows that the size changes but assumes the next numeric index still refers to the next original element. These are related difficulties with different immediate repair needs.
Ask each learner to draw a list with index labels. Remove the item at index zero and redraw the remaining labels. If the student leaves a permanent empty slot at zero, return to the method's behaviour. If they relabel correctly but still increment past the shifted item, focus on how the cursor and list change together.
An index is a position in the current list, not an identity permanently attached to an original element. The string oak can begin at index one and later occupy index zero. Keeping that distinction visible is the central move. Without it, a trace can preserve the variable values while imagining the wrong list.
Do not start by giving the instruction to loop backwards and moving on. That can repair one piece of code while leaving the learner unable to explain why it works or recognise a different requirement. First expose the state change, then compare solutions built around it.
A useful diagnostic question is: which element has not yet been considered, and where is it now? Ask it after a removal, after retaining an item and at termination. The answer should connect the current index with the actual list, rather than describe a memorised sequence of numbers.
CHAPTER 3 OF 15 · Understand the moving collection
3. Understand what removing by index changes
The current College Board Java Quick Reference describes remove(int index) as removing the element at the specified position, returning that element and shifting later elements one place to the left. The list's size decreases by one. We will use that documented method, not a different overload or operation.
Suppose the list is [elm, oak, ash, fir, maple]. Its valid indices are zero through four. Removing index zero returns elm and leaves [oak, ash, fir, maple]. The current size is four, and oak now occupies zero. There is no gap waiting to be visited before oak.
Removing a later item also changes the labels to its right. If ash is removed from the original list at index two, fir moves from three to two and maple from four to three. Elements before the removed position keep their indices. That asymmetry explains why traversal direction matters.
The removed value and the method's side effect answer different questions. A programme might use the returned string, but the index problem exists even when that return value is ignored. The learner should describe both what the call returns and what it changes in the collection.
Now compare set(index, value). Replacing an element does not shrink the list or shift the later positions. A traversal appropriate for replacement may therefore behave differently when set is changed to remove. Small edits to the operation can change the algorithm's state model substantially.
CHAPTER 4 OF 15 · Repair the traversal
4. Trace a forward loop that skips matches
Here is deliberately faulty original code for our all-matches task. Assume words initially contains [elm, oak, ash, fir, maple]. The condition correctly identifies three-character strings, but the overall traversal does not examine every required element after the list changes.
for (int i = 0; i < words.size(); i++) {
if (words.get(i).length() == 3) {
words.remove(i);
}
}
At index zero, elm matches and is removed. Oak shifts to zero, ash to one, fir to two and maple to three. The for loop's update then changes i to one. The next tested element is ash, not oak. The newly shifted oak has been skipped.
Ash also matches and is removed at its current index one. The remaining list becomes [oak, fir, maple]. The update changes i to two, which now refers to maple. Maple does not match. After the next update, i is three and the loop terminates because the current size is also three.
| Step in the faulty loop | Index being tested | List after the decision | Next index after update |
|---|---|---|---|
| Remove elm | 0 | [oak, ash, fir, maple] | 1 |
| Remove ash | 1 | [oak, fir, maple] | 2 |
| Retain maple | 2 | [oak, fir, maple] | 3, so the loop stops |
The final list still contains oak and fir, both of length three. This is not a failure of the predicate. The cursor advanced after removal while an unprocessed element had moved into its previous position. A different input might conceal the same defect, which is why one apparently successful run is insufficient evidence.
Ask the learner to identify the first moment the intended reasoning breaks. It is the transition after removing elm and incrementing i. Finding that moment is more useful than merely stating that the final answer is wrong. It reveals which relationship a repair must preserve.
CHAPTER 5 OF 15 · Repair the traversal
5. Repair the forward traversal deliberately
Use a while loop so that advancing the index becomes an explicit decision. If the current element is removed, keep i unchanged because a new unprocessed element may have shifted into that position. If the element is retained, advance i because that position has now been processed successfully.
int i = 0;
while (i < words.size()) {
if (words.get(i).length() == 3) {
words.remove(i);
} else {
i++;
}
}
On our original input, elm, oak, ash and fir are successively removed from index zero. Each removal makes the next unprocessed string occupy zero. Maple is then retained, so i becomes one. The current size is one, making the loop condition false. The final list is [maple].
Why does keeping the index unchanged not create an infinite loop here? A removal reduces the size by one. A retention increases i by one. Either way, the number of current elements from i onward decreases. The loop is making progress even when the index itself does not change.
This explanation depends on the actual operations. If a learner deletes the removal call but still leaves i unchanged in that branch, the progress argument no longer applies. If another operation adds elements, the reasoning must be reconsidered. A useful algorithm explanation describes the current programme, not merely its resemblance to a familiar pattern.
Check the final condition too. At termination, i equals the current size in this traversal, so no unprocessed positions remain. The result should also satisfy the task's content and order requirements. Termination alone does not prove that the right strings were removed.
Measure the work that remains
For the corrected forward loop, define the remaining work as the current size minus i. Initially, that quantity equals the number of strings in the original list. It is a nonnegative integer whenever the loop condition is considered, and the body runs only while it is positive.
If the current string is removed, size decreases by one while i stays fixed. If it is retained, size stays fixed while i increases by one. In either branch, the remaining-work quantity decreases by exactly one. Under our stated task and operations, the loop therefore processes each original string once and eventually reaches zero remaining work.
This argument is stronger than observing that one example stops. It explains why the two branches both make progress even though the index changes in only one branch. It also identifies the assumptions: the code does not insert new elements, and checking a string's length does not itself modify the list.
Ask the learner what would break the argument. If the retention branch forgot to increment i, remaining work would not decrease. If the removal branch both removed and incremented, remaining work could decrease by two, concealing an unprocessed element. The measure helps distinguish nontermination from skipped work.
CHAPTER 6 OF 15 · Repair the traversal
6. Understand why a backward traversal also works
Another solution begins at the last valid index and moves towards zero. Removing the current element shifts only elements at higher indices. Those positions belong to the portion already considered, so the unprocessed lower indices remain available without a skipped element moving behind the cursor.
for (int i = words.size() - 1; i >= 0; i--) {
if (words.get(i).length() == 3) {
words.remove(i);
}
}
On the original list, the traversal first retains maple at index four. It then removes fir at three, ash at two, oak at one and elm at zero. Maple shifts left as earlier items disappear, but it has already been considered. The final list is again [maple].
The explanation is not that backward loops are universally better. They fit this removal task because of the direction in which remove shifts elements. A different task, such as comparing a value with an evolving prefix, could require a different organisation. Start from the specification and state changes.
Backward traversal also preserves the relative order of the retained items because ArrayList removal preserves the order of the remaining elements. We are not swapping a last item into the deleted position. That alternative would need a separate argument and could violate an order-preservation requirement.
Ask the learner to explain the method with a new list containing two retained strings separated by matches. They should identify both the unprocessed lower portion and the already considered higher portion after each removal. That explanation demonstrates more understanding than simply reproducing the loop header.
CHAPTER 7 OF 15 · Make the algorithm dependable
7. State what has already been processed
An invariant describes a relationship that remains true at a particular point in each repetition. For the corrected forward loop, before the condition is tested, every position below i has been processed and contains a retained string. The positions from i onward are the current unprocessed portion.
If the current string is retained, increasing i moves that string into the processed portion. If it is removed, the processed portion stays unchanged while the unprocessed portion becomes shorter. The string that shifts into i still belongs to the unprocessed portion and will be considered next.
The faulty for loop breaks that relationship after a removal and unconditional increment. It puts an unexamined shifted string behind the cursor, inside the region the student is treating as processed. The invariant gives a precise explanation for the skip, rather than relying on the accident of our chosen words.
For the backward version, the useful statement changes. The positions above i have already been considered, while positions zero through i remain to be examined. Removal at i does not alter lower positions. Decrementing i therefore reaches the next unprocessed location.
Sengkang's guide to searching algorithms and their invariants develops the broader question of what a search or traversal preserves. Here, the loop needs a clear account of which work is complete and which work remains while the collection's positions can change.
Use ordinary language before demanding a formal proof. Ask the student to point to the two regions on a drawn list and describe why the next operation preserves them. A short correct explanation can then be made more precise. Vocabulary should clarify a relationship the learner can already inspect.
A termination argument and a correctness argument answer different questions. The faulty forward loop also stops, but it has skipped work. A decreasing numeric expression is therefore not enough on its own. You also need the invariant that explains why the region treated as processed really contains only elements already considered. Together, those arguments connect eventual stopping with the task's required result.
For a comparison with numerical work, the IB Mathematics guide to checking a result against bounds explains why passing a broad restriction does not establish an exact answer. An enclosing interval and a loop invariant are different tools; in each case, explain what the check establishes and what it leaves unresolved.
CHAPTER 8 OF 15 · Make the algorithm dependable
8. Handle boundaries through the same reasoning
An empty list is an important check. In the forward while loop, i begins at zero and the current size is zero, so the body does not run. In the backward loop, the initial index is minus one, so the condition i >= 0 is false. Neither version attempts to access an element of the empty list.
A one-element list gives two different useful cases. If the only string matches, removal leaves the list empty. If it does not match, it remains. Explain both without assuming that a loop body must run a second time. The boundary behaviour should follow naturally from the condition and state change.
All-match input checks repeated removal. In the forward version, the index can remain at zero while the list shrinks to empty. No-match input checks that retention advances the cursor until every element is considered. These cases test different branches of the algorithm's progress argument.
The last position matters too. After retaining an element at the last valid index in the forward loop, incrementing i makes it equal to size, so the next test stops the loop before get is called again. Using <= instead of < would permit an invalid access at size.
Do not treat these as isolated exceptions to memorise. They are consequences of the same rule: the body may access an element only when the current index is valid, and each branch must move the algorithm towards completion. A coherent state model makes the boundary cases less mysterious.
Trace the danger of an unchanged upper boundary
Consider the short input [elm, maple]. Suppose a learner stores the original size, two, then runs a forward loop to that stored boundary while removing matches. Removing elm leaves [maple], whose only valid index is zero. If the cursor then becomes one, it still lies below the stored boundary two but is no longer a valid current index.
That example exposes a different failure from the earlier skipped-match trace. Accessing position one now causes an invalid-index problem. The current list, not its original length, determines which accesses are valid after deletion. A stored historical number cannot silently substitute for that changing boundary.
However, replacing the stored boundary with the current size does not repair every issue. Our first faulty loop already used the current size and still skipped adjacent matches. Boundary validity and complete traversal are separate obligations. A successful solution must preserve both.
Ask a student to name which obligation a proposed edit addresses. Changing <= to < may prevent an access at size, while changing the update after removal may prevent a skip. A precise diagnosis makes it easier to avoid celebrating a partial repair as a complete solution.
CHAPTER 9 OF 15 · Make the algorithm dependable
9. Read the method specification before coding
Our task changes the supplied list in place. That means another reference to the same ArrayList observes the changed contents. A method that constructs and returns a separate filtered list answers a different specification, even if the new list contains the intended retained strings.
Suppose the original exercise also asks for the number removed. A counter should increase in the removal branch, not in every repetition. The number of iterations and number of removals are not necessarily the same. On the original five-word list, four removals leave one retained string.
public static int removeLengthThree(ArrayList<String> words) {
int removed = 0;
int i = 0;
while (i < words.size()) {
if (words.get(i).length() == 3) {
words.remove(i);
removed++;
} else {
i++;
}
}
return removed;
}
This original method assumes a non-null list of non-null strings and that ArrayList is imported from java.util. It is shown as a method to place inside an appropriate class, not as a complete standalone programme. The return value reports the count; the list modification is a separate effect.
If a requirement asks for only the first matching string to be removed, the all-matches loop goes too far. If it asks to retain the original list, in-place deletion violates that condition. Before evaluating the code, write down what should change, what should remain and what the method should return.
This habit is useful in free-response preparation because a technically valid fragment can still miss the requested behaviour. Read the supplied class, method header, preconditions and postconditions. Do not replace them with the specification of a similar exercise you remember.
Distinguish the return value from the changed object
Imagine that two variables refer to the same ArrayList object. Calling our removal method through one variable changes that object, so the other reference also observes the new contents. Returning the number removed does not create a separate list or undo the change. These are different parts of the method's behaviour.
In a paper exercise, draw one list object and two arrows pointing to it. Then remove an element from the object and inspect what each reference reaches. Drawing two independent copies would misrepresent the situation and could lead the learner to predict unchanged contents through the second reference.
This matters when a problem asks what a caller observes after a method returns. The returned integer might be four while the supplied list now contains only maple. Reporting one result does not answer the other question. Read the requirement carefully and explain the relevant effect.
Do not generalise this into a claim that every method receiving a list must change it. The method body determines what occurs. A method that only reads the list, or constructs a separate result without modifying the original, has a different effect. Trace the operations actually present rather than relying on the parameter's type alone.
CHAPTER 10 OF 15 · Test and explain the behaviour
10. Choose inputs that can expose the bug
A useful test is chosen to distinguish the intended behaviour from a plausible incorrect implementation. Adjacent matches are important here because the faulty forward loop can remove a matching element and then skip the matching element shifted into its place. A list with only isolated matches may conceal that weakness.
Try [sun, moon, star, sky] with the length-three condition. The intended result is [moon, star]. Then try [elm, oak, maple], where two matches occur together at the start. The intended result is [maple]. Explain which case is more directly aimed at the shifting-and-skipping problem.
Include empty, one-match, one-retained, all-match and no-match lists. Also include matches at the end and a mixture that retains several strings. The purpose is not to accumulate an impressive number of examples. Each input should test a particular condition or branch in the reasoning.
Write the expected result before executing or tracing the code. Otherwise, it is easy to accept whatever output appears as plausible. For an order-preservation requirement, compare the sequence of retained elements, not just their number. The right count can coexist with the wrong contents or order.
After a failure, locate the first state that differs from the intended reasoning. An incorrect final list is a symptom; the first skipped unprocessed element explains the mechanism. A diagnostic test is most useful when it helps the learner identify that transition and repair the appropriate decision.
CHAPTER 11 OF 15 · Test and explain the behaviour
11. Keep removal, replacement and selection distinct
Removing a string changes the collection's size and later indices. Replacing it with another string changes an element's value while preserving the number of positions. Selecting matching strings into a new list creates another collection. These operations may appear in related tasks, but they require different state reasoning.
For a replacement example, imagine changing every three-character string to the word short. A forward for loop can advance after each replacement because later elements have not shifted. Copying that update rule into a removal task without reconsidering the operation creates the bug we traced earlier.
For a new-list example, traverse the original list and add only the nonmatching strings to a separate result list. The original indices remain stable because the original list is not being structurally changed. That can be a useful design when the specification permits it, but it does not satisfy an instruction to modify the supplied list in place.
College Board warns against adding or removing elements while traversing an ArrayList with an enhanced for loop. Changing the size can produce a ConcurrentModificationException. Use a traversal designed for the required structural changes, and keep the course's stated method behaviours in view.
Do not infer that every other unfamiliar failure is this same bug. An incorrect predicate, a null element, an invalid index and a skipped shifted element are different causes. State the practice assumptions and inspect the first failing decision. A correct repair should address the behaviour actually observed.
CHAPTER 12 OF 15 · Test and explain the behaviour
12. Teach the reasoning before increasing speed
Begin with paper cards representing the strings and separate index labels. Remove a card, slide the remaining cards left and relabel the positions. Then place a cursor card at i. This physical representation can make the difference between element identity and current position visible without adding Java syntax.
Next trace the faulty loop and ask the learner to predict the next tested string before moving the cursor. If the prediction is wrong, discuss the list and index updates separately. The goal is an explanation of the transition, not a faster guess at the final output.
After comparing the corrected forward and backward versions, close the examples. Ask the student to reconstruct one solution and justify both branches. Then change the predicate or input pattern. A learner who succeeds only with the original tree names may have memorised a trace rather than understood the traversal.
Parents can ask what work remains after the current operation. Teachers can ask which statement is guaranteed before each repetition. Tutors can use unrelated examples to repair the state model. Support should leave the student able to write and explain their own response under the actual assessment requirements.
Record the difficulty precisely. “Skipped a shifted adjacent match after incrementing” points towards a specific repair. “Bad at loops” does not. An effective practice session ends with a new input and a short explanation of why the repaired algorithm handles it.
CHAPTER 13 OF 15 · Transfer the loop reasoning
13. Transfer the idea to a fresh task
Use this original practice specification: remove every empty string from a non-null ArrayList of non-null strings, preserve the other strings' order and return the number removed. Before writing code, explain what changes from the length-three example and what stays the same.
The predicate changes to a check for length zero. The shifting behaviour of remove(int index) does not change. The corrected forward traversal can therefore keep its index policy: remove without advancing when the current string matches, and advance when retaining it. The counter still increases only on removal.
Try [a, empty string, empty string, bb, empty string]. The intended result is [a, bb] and the returned count is three. Here empty string is an explanatory label for the Java string literal with no characters, not the words empty string stored as a value. Write the actual representation carefully in code.
Now change the task to remove only the first empty string. That is a different specification; continuing through all matches would be incorrect. Explain how stopping after the first successful removal changes the algorithm's goal. Do not keep the all-matches solution merely because its predicate is familiar.
Finally, change the operation from removal to replacing empty strings with a label. The list's size now stays fixed, so a different update policy becomes appropriate. The transferable understanding is how the operation affects unprocessed work, rather than a permanent instruction to keep the same index whenever a condition is true.
CHAPTER 14 OF 15 · Transfer the loop reasoning
14. Questions students often ask
Is the condition wrong when adjacent matches remain?
Not necessarily. In our faulty example, the condition correctly recognises a three-character string. The failure occurs after removal, when the unconditional increment skips the shifted element. Trace the first incorrect transition before changing the predicate.
Why does the corrected while loop sometimes keep i unchanged?
Because removing the current element shifts the next unprocessed element into that position. Keeping the index lets the next iteration examine it. The list has become shorter, so the loop can still make progress without changing i in that branch.
Is backward traversal always the best solution?
No. It fits this removal task because deletion shifts later elements while leaving lower indices unchanged. Other specifications and operations may require a different approach. Explain the state relationship that makes your selected traversal appropriate.
Can I keep the original size in a variable and loop to it?
That stored number does not shrink with the list. Using it as an access boundary can permit an invalid index after removals. If you store an original size for a separate purpose, distinguish that purpose from the current valid-index range.
Is replacing an item equivalent to removing it?
No. Replacement preserves size and later indices; removal changes both. Read the method behaviour and the task. A loop update that works for one operation may be inappropriate for the other.
Do I need to catch an exception to solve this exercise?
The examples are designed to avoid invalid access through correct conditions and index reasoning. Adding an exception handler would not repair a traversal that skips required elements. Address the algorithm's specification and state changes directly.
Are these official AP questions or guaranteed scoring patterns?
No. They are original teaching examples aligned with documented collection behaviour and the current course framework. Use College Board's public materials and your teacher's guidance for the complete assessment scope. No particular examination result is promised.
CHAPTER 15 OF 15 · Transfer the loop reasoning
15. Sources and the next code-tracing question
Primary documentation checked on 24 October 2026 includes College Board's AP Computer Science A course page, the Course and Exam Description effective fall 2025, and its published clarifications. The course document's topics 4.8–4.10 and Java Quick Reference establish the relevant method and traversal context, including the behaviour of removal by index.
Priya and Ben are fictional learners. The tree-name lists, code, traces and practice tasks are original independent examples. They do not reproduce secure classroom or examination material. The snippets assume the stated input conditions and should be read with the method's specification; they are not a complete application.
For the next unfamiliar loop, pause after each structural change and ask where the next unprocessed element is now. If the learner can answer that question, explain the progress towards termination and check the required final state, the code becomes a sequence of defensible decisions rather than a pattern to memorise.