4.10 Implementing ArrayList Algorithms
Implement algorithms with ArrayLists to search, count, calculate results, and update a resizable collection.
1. LxD Cycle Process
Empathize: Students can call add, get, set, remove, and size correctly in isolation, but freeze when a free-response question asks for “the largest score” or “how many names match,” because that requires combining those single steps inside a loop. A second failure mode shows up only at runtime: removing elements while traversing forward silently skips the element right after the one just removed.
Define:
- POV: CSA students need to see
ArrayListalgorithms as the same traversal shape used for arrays, but withsize()andget(i)in place of.lengthand[i], because the loop skeleton for max/min, searching, filtering, and removing is otherwise identical to what they already know. - Learning Goal: Students will write traversal algorithms over an
ArrayListto find a maximum or minimum, search and count withindexOf/contains/a counting loop, build a filtered list, and remove elements while traversing without skipping any.
Ideate:
- HMW Question: How might we make the “start
maxatget(0), not at0” rule feel necessary instead of arbitrary? - HMW Question: How might we make a skipped element during forward removal visible, so the backward-traversal fix feels earned rather than memorized?
- Activity: Trace a buggy
maxValueBuggyon an all-negative list, then trace a forward-removal loop step by step and watch it skip a value.
Prototype & Test: Lesson authors: add what happened in your trial run and what you changed.
2. Lesson Plan
Learning Objective: Write traversal algorithms over an ArrayList to find extremes, search, filter, and safely remove elements.
Success Criteria: You can explain why maxValue starts comparing at index 1, use indexOf/contains versus a counting loop appropriately, build a new filtered list without changing the original, and traverse backward when removing multiple elements by index.
Before you start: ArrayList methods from 4.8 (add, get, set, remove, size) and indexed for loops.
Tech Talk (5 minutes)
add, get, set, remove, and size are single steps. An algorithm combines those steps in a loop to answer a real question: What is the highest score? How many names match? Which elements should stay?
The traversal shape carries over directly from arrays; only the bound and the access method change:
for (int i = 0; i < scores.size(); i++) { // size(), not .length
System.out.println(scores.get(i)); // get(i), not scores[i]
}
Two habits keep these algorithms correct. First, when finding a maximum or minimum, start the accumulator at nums.get(0) and loop from index 1, so a list of only negative numbers is not compared against an assumed starting value like 0. Second, when removing elements by index while traversing, go from the last index down to 0; removing a later index never shifts the position of an earlier, unvisited index, so nothing gets skipped.
From the College Board
AP CSA Unit 4, Topic 4.10 focuses on implementing ArrayList algorithms. Quoted from the course and exam description (College Board, 2025, p. 113):
- 4.10.A “Develop code for standard and original algorithms for a particular context or specification that involve
ArrayListobjects and determine the result of these algorithms.” - 4.10.A.1 “There are standard
ArrayListalgorithms that utilize traversals to: determine a minimum or maximum value; compute a sum or average; determine if at least one element has a particular property; determine if all elements have a particular property; determine the number of elements having a particular property; access all consecutive pairs of elements; determine the presence or absence of duplicate elements; shift or rotate elements left or right; reverse the order of the elements; insert elements; delete elements.” - 4.10.A.2 “Some algorithms require multiple String, array, or
ArrayListobjects to be traversed simultaneously.”
The related traversal essential knowledge from Topic 4.9 explains why the removal direction matters (College Board, 2025, p. 112):
- 4.9.A.2 “Deleting elements during a traversal of an
ArrayListrequires the use of special techniques to avoid skipping elements.” - 4.9.A.4 “Changing the size of an
ArrayListwhile traversing it using an enhanced for loop can result in aConcurrentModificationException. Therefore, when using an enhanced for loop to traverse anArrayList, you should not add or remove elements.”
3. Reference Guide
Key Vocabulary
| Term | Definition | Example |
|---|---|---|
| Traversal | Visiting every element of a collection, usually with a loop. | for (int i = 0; i < nums.size(); i++) |
| Accumulator | A variable updated across iterations to build a running result. | max, count, kept |
| indexOf(obj) | Returns the index of the first match, or -1 if none. |
roster.indexOf("Amir") |
| contains(obj) | Returns true if any element matches. |
roster.contains("Jordan") |
| ConcurrentModificationException | Runtime error from adding/removing during an enhanced for loop. |
Removing inside for (int n : nums) |
Standard ArrayList Algorithm Categories (EK 4.10.A.1)
- Minimum or maximum value
- Sum or average
- At least one element with a property
- All elements with a property
- Count of elements with a property
- Consecutive pairs of elements
- Presence or absence of duplicates
- Shift or rotate elements
- Reverse the order of elements
- Insert or delete elements
Safe vs. Unsafe Traversal for Removal
| Traversal | Safe to remove while traversing? |
|---|---|
Indexed for loop, forward (i increasing) |
Only one removal, or accept skipped elements |
Indexed for loop, backward (i decreasing to 0) |
Yes — earlier indices are never shifted |
Enhanced for loop (for (int n : nums)) |
No — throws ConcurrentModificationException if the list changes size |
4. Code Examples
A. Warm-up: The Traversal Shape Carries Over
Run the cell below, then answer the questions that follow.
int[] arrayScores = {70, 85, 90};
ArrayList<Integer> listScores = new ArrayList<Integer>();
listScores.add(70);
listScores.add(85);
listScores.add(90);
System.out.println("array length = " + arrayScores.length);
System.out.println("list size = " + listScores.size());
- What loop condition visits every index of
arrayScores? - What loop condition visits every index of
listScores? - What changes between the two loops? What stays the same?
Check your reasoning
i < arrayScores.lengthi < listScores.size()- The bound changes from a field to a method call. The overall shape (start at
0, read one element per iteration, stop before the bound) stays the same.
B. Finding a Maximum
Contract: nums contains at least one element.
Predict: What does maxValue return for [4, 9, 2, 9, 5]? Then run the cell.
public static int maxValue(ArrayList<Integer> nums) {
int max = nums.get(0);
for (int i = 1; i < nums.size(); i++) {
if (nums.get(i) > max) {
max = nums.get(i);
}
}
return max;
}
ArrayList<Integer> sample = new ArrayList<Integer>();
sample.add(4);
sample.add(9);
sample.add(2);
sample.add(9);
sample.add(5);
System.out.println(maxValue(sample));
Check your reasoning
| i | nums.get(i) | max after this step |
|---|---|---|
| start | - | 4 |
| 1 | 9 | 9 |
| 2 | 2 | 9 |
| 3 | 9 | 9 |
| 4 | 5 | 9 |
maxValue returns 9. Starting max at nums.get(0) and looping from index 1 avoids comparing an element to itself.
You decide: Why does this method start max at nums.get(0) instead of 0? Run the cell below, which starts max at 0 instead, on a list of only negative numbers.
public static int maxValueBuggy(ArrayList<Integer> nums) {
int max = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums.get(i) > max) {
max = nums.get(i);
}
}
return max;
}
ArrayList<Integer> negatives = new ArrayList<Integer>();
negatives.add(-4);
negatives.add(-9);
negatives.add(-2);
System.out.println(maxValueBuggy(negatives));
Reveal the answer
maxValueBuggy returns 0, a value that never appears in negatives. Starting at 0 assumes a non-negative value is always available for comparison, which is not true here since every real element is less than 0.
C. Searching: indexOf, contains, and Counting Matches
| Method call | Returns |
|---|---|
indexOf(obj) |
The index of the first element equal to obj, or -1 if none matches |
contains(obj) |
true if some element is equal to obj, otherwise false |
Predict: What are firstIndex and hasJordan? Then run the cell.
ArrayList<String> roster = new ArrayList<String>();
roster.add("Amir");
roster.add("Priya");
roster.add("Amir");
int firstIndex = roster.indexOf("Amir");
boolean hasJordan = roster.contains("Jordan");
System.out.println("firstIndex = " + firstIndex);
System.out.println("hasJordan = " + hasJordan);
Check your reasoning
firstIndex is 0, the position of the first "Amir". hasJordan is false; "Jordan" never appears in roster. Both methods compare content with .equals, not object identity.
Both methods stop as soon as one match is found and cannot report how many matches exist in total. Counting every match needs a loop.
Predict: How many times does "Amir" appear? Then run the cell.
public static int countMatches(ArrayList<String> names, String target) {
int count = 0;
for (int i = 0; i < names.size(); i++) {
if (names.get(i).equals(target)) {
count++;
}
}
return count;
}
ArrayList<String> roster2 = new ArrayList<String>();
roster2.add("Amir");
roster2.add("Priya");
roster2.add("Amir");
System.out.println(countMatches(roster2, "Amir"));
Pause: Why does this method call .equals instead of using ==?
Check your reasoning
== on Strings compares references, not the characters they contain. Two different String objects holding the same text are not guaranteed to be ==, but .equals always compares content correctly.
D. Building a Filtered List
An algorithm can traverse one list while building a second one, keeping only the elements that satisfy some condition.
Predict: What does keepAtLeast return for nums = [3, 8, 1, 10, 6] and threshold = 6? Then run the cell.
public static ArrayList<Integer> keepAtLeast(ArrayList<Integer> nums, int threshold) {
ArrayList<Integer> kept = new ArrayList<Integer>();
for (int num : nums) {
if (num >= threshold) {
kept.add(num);
}
}
return kept;
}
ArrayList<Integer> nums = new ArrayList<Integer>();
nums.add(3);
nums.add(8);
nums.add(1);
nums.add(10);
nums.add(6);
System.out.println(keepAtLeast(nums, 6));
Check your reasoning
[8, 10, 6]. The enhanced for loop visits every element of nums in order without touching nums itself; only the new list kept is built up.
Why this is safe: the enhanced for loop reads nums but never adds to or removes from it. nums keeps its original size and order the entire time.
E. The Removal Trap: Modifying While Traversing
Removing elements from the same list you are traversing changes what the next iteration sees. Run the cell below and check whether every 2 gets removed.
ArrayList<Integer> valuesBuggy = new ArrayList<Integer>();
valuesBuggy.add(5);
valuesBuggy.add(2);
valuesBuggy.add(2);
valuesBuggy.add(9);
for (int i = 0; i < valuesBuggy.size(); i++) {
if (valuesBuggy.get(i) == 2) {
valuesBuggy.remove(i);
}
}
System.out.println(valuesBuggy);
Check your reasoning
| i | values before this step | action |
|---|---|---|
| 0 | [5, 2, 2, 9] | 5 != 2, no removal |
| 1 | [5, 2, 2, 9] | remove index 1 |
| 2 | [5, 2, 9] | values.get(2) is 9, no removal |
Not every 2 is removed. After removing index 1, the second 2 shifts into index 1, but i advances to 2 and skips over it. The final list is [5, 2, 9], still containing a 2.
Do not traverse an ArrayList with an enhanced for loop while adding to or removing from that same list. Its structure is expected to stay unchanged during that kind of loop, and Java can throw a runtime error (ConcurrentModificationException) if it is changed anyway.
Fixing it: traverse backward
Traversing from the last index to the first means every removal only affects indices already visited. Run the corrected version below.
ArrayList<Integer> valuesFixed = new ArrayList<Integer>();
valuesFixed.add(5);
valuesFixed.add(2);
valuesFixed.add(2);
valuesFixed.add(9);
for (int i = valuesFixed.size() - 1; i >= 0; i--) {
if (valuesFixed.get(i) == 2) {
valuesFixed.remove(i);
}
}
System.out.println(valuesFixed);
Check your reasoning
| i | values before this step | action |
|---|---|---|
| 3 | [5, 2, 2, 9] | 9 != 2, no removal |
| 2 | [5, 2, 2, 9] | remove index 2 |
| 1 | [5, 2, 9] | remove index 1 |
| 0 | [5, 9] | 5 != 2, no removal |
The final list is [5, 9]. Removing a later index first never changes the position of an earlier, still-unvisited index.
5. Hacks & Practice Tasks
Prepare your submission IPYNB
- Create a new notebook in your portfolio homework area:
_notebooks/homework. - Add one raw cell at the top with the frontmatter:
---
layout: post
codemirror: true
title: Implementing ArrayList Algorithms HW
categories: [Java]
lesson_language: Java
lesson_topic: Implementing ArrayList Algorithms HW
lesson_part: interactive
lesson_type: lesson
permalink: /csa/unit_04/4_10-hw
author: yourGithubID
---
- Add code cells for the Popcorn Hack and both parts of the Homework Hack. Make sure every cell runs with visible output.
- Submit the link to your published page at the bottom of this page, and paste this in the description box:
Lesson: CSA 4.10 Implementing ArrayList Algorithms
Popcorn: minValue implemented and tested against all three sample calls
Homework Part A: countAtLeast implemented and tested against all three sample calls
Homework Part B: removeNegatives implemented and tested against all three sample calls
Submission Safety Rules (Read First)
- One method per cell for the Hacks, ending with a test cell that prints its results.
- Run each cell and leave the output showing.
- Traverse backward when the contract requires removing elements in place.
- Use
##headings or smaller.
Popcorn Hack (In-Class)
Write minValue(ArrayList<Integer> nums) that returns the smallest value in nums. Model it after maxValue.
Contract: nums contains at least one element.
| Call | Expected result |
|---|---|
minValue([4, 9, 2, 9, 5]) |
2 |
minValue([7]) |
7 |
minValue([-3, -1, -9]) |
-9 |
Write your solution in the cell below, then run the test cell after it.
public static int minValue(ArrayList<Integer> nums) {
// Find and return the smallest value.
return 0;
}
ArrayList<Integer> minTest1 = new ArrayList<Integer>();
minTest1.add(4); minTest1.add(9); minTest1.add(2); minTest1.add(9); minTest1.add(5);
System.out.println(minValue(minTest1)); // expect 2
ArrayList<Integer> minTest2 = new ArrayList<Integer>();
minTest2.add(7);
System.out.println(minValue(minTest2)); // expect 7
ArrayList<Integer> minTest3 = new ArrayList<Integer>();
minTest3.add(-3); minTest3.add(-1); minTest3.add(-9);
System.out.println(minValue(minTest3)); // expect -9
Reveal a solution after writing yours
public static int minValue(ArrayList<Integer> nums) {
int min = nums.get(0);
for (int i = 1; i < nums.size(); i++) {
if (nums.get(i) < min) {
min = nums.get(i);
}
}
return min;
}
Self-check: Did you start min at nums.get(0) and loop from index 1? Did you flip the comparison from > to <?
Reflect
- What I changed from
maxValueto getminValue: - My additional test call and its expected result:
Homework Hack
Part A — count at least. Write countAtLeast(ArrayList<Integer> nums, int threshold) that returns how many elements of nums are greater than or equal to threshold. Model it after countMatches.
Contract: nums may be empty.
| Call | Expected result |
|---|---|
countAtLeast([3, 8, 1, 10, 6], 6) |
3 |
countAtLeast([1, 2, 3], 5) |
0 |
countAtLeast([], 0) |
0 |
Write your solution in the cell below, then run the test cell after it.
public static int countAtLeast(ArrayList<Integer> nums, int threshold) {
// Count and return how many elements are >= threshold.
return 0;
}
ArrayList<Integer> countTest1 = new ArrayList<Integer>();
countTest1.add(3); countTest1.add(8); countTest1.add(1); countTest1.add(10); countTest1.add(6);
System.out.println(countAtLeast(countTest1, 6)); // expect 3
ArrayList<Integer> countTest2 = new ArrayList<Integer>();
countTest2.add(1); countTest2.add(2); countTest2.add(3);
System.out.println(countAtLeast(countTest2, 5)); // expect 0
ArrayList<Integer> countTest3 = new ArrayList<Integer>();
System.out.println(countAtLeast(countTest3, 0)); // expect 0
Reveal a solution after writing yours
public static int countAtLeast(ArrayList<Integer> nums, int threshold) {
int count = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums.get(i) >= threshold) {
count++;
}
}
return count;
}
Self-check: Does your method return 0 for an empty list without a special case? Did you use >= and not >?
Reflect
- What I changed from
countMatchesto getcountAtLeast: - My additional test call and its expected result:
Part B — remove negatives safely. Write removeNegatives(ArrayList<Integer> nums) that removes every negative value from nums in place, without skipping any element.
Contract: Modify nums directly; do not return a new list. nums may contain zero, one, or many negative values, and may already contain none.
| Starting list | List after the call |
|---|---|
[3, -1, 5, -2, 0] |
[3, 5, 0] |
[-1, -2, -3] |
[] |
[4, 5] |
[4, 5] |
Write your solution in the cell below, then run the test cell after it.
public static void removeNegatives(ArrayList<Integer> nums) {
// Traverse in a direction that is safe for removal.
}
ArrayList<Integer> test1 = new ArrayList<Integer>();
test1.add(3); test1.add(-1); test1.add(5); test1.add(-2); test1.add(0);
removeNegatives(test1);
System.out.println(test1); // expect [3, 5, 0]
ArrayList<Integer> test2 = new ArrayList<Integer>();
test2.add(-1); test2.add(-2); test2.add(-3);
removeNegatives(test2);
System.out.println(test2); // expect []
ArrayList<Integer> test3 = new ArrayList<Integer>();
test3.add(4); test3.add(5);
removeNegatives(test3);
System.out.println(test3); // expect [4, 5]
Reveal a solution after writing yours
public static void removeNegatives(ArrayList<Integer> nums) {
for (int i = nums.size() - 1; i >= 0; i--) {
if (nums.get(i) < 0) {
nums.remove(i);
}
}
}
Self-check: Does your loop start at the last index and move toward 0? Does it leave every non-negative value in its original order?
Reflect
- The direction my loop traverses, and why:
- What happens if I traverse forward instead, on
[-1, -2, 3]:
Exit Ticket
- Why does
maxValuestart comparing at index1instead of index0? - What is the key difference between
indexOfand a loop that counts every match? - Why is it safe to build a new list with an enhanced
forloop over the original, but unsafe to remove from the original during that same loop? - When removing multiple elements by index from an
ArrayList, why does traversing backward avoid skipped elements?
Exit-ticket key
maxis initialized withnums.get(0), so comparisons should begin at the next element to avoid comparing an element to itself.indexOfstops at the first match and reports only its position. A counting loop checks every element and can report how many matches exist in total.- Building a new list only reads the original; its size and order never change during the loop. Removing from the original changes its size and shifts later elements, which the enhanced
forloop does not expect. - Removing a later index does not change the position of any earlier, unvisited index, so every index scheduled to be checked still gets checked exactly once.
6. Grading Plan (1 Point Total)
| Part | Points | What earns the points |
|---|---|---|
Popcorn (minValue) |
0.25 | Correctly models maxValue, starting at get(0) and comparing with <. |
Homework Part A (countAtLeast) |
0.25 | Correct count for all three sample calls, including the empty-list case. |
Homework Part B (removeNegatives) |
0.25 | Traverses backward and leaves every non-negative value in its original order. |
| Code quality and reflection | 0.25 | Every cell runs with visible output, and the Reflect prompts are answered in the student’s own words. |
| Total | 1.0 |
Quick Validation Checklist
- Every Hack cell runs and prints visible output that matches the expected results.
minValuestarts its accumulator atnums.get(0), not0.removeNegativestraverses from the last index to0.- All three Reflect prompts are answered.
- The Exit Ticket questions are answered before submitting.
7. Lesson Revisions & Feedback Evidence
Feedback Received: Lesson authors: what your peers said in the practice run.
Revision Made: Lesson authors: what you changed because of it.
References
College Board. (2025). AP Computer Science A course and exam description [Effective fall 2025]. See Topic 4.10, Implementing ArrayList Algorithms, p. 113, and Topic 4.9, ArrayList Traversals, p. 112.
- CSAwesome2, Topic 4.10: Implementing ArrayList Algorithms (Runestone Academy). Covers the standard traversal algorithms (max/min, search, filter) and the removal-while-traversing trap.
- CSAwesome2, Topic 4.9: ArrayList Traversals (Runestone Academy). Covers enhanced
for,while, and indexed traversal ofArrayList, plusConcurrentModificationException. - CSAwesome2, 4.33 Code Practice with ArrayLists (Runestone Academy). Extra practice problems that combine
ArrayListmethods into small algorithms. - Implementing ArrayList Algorithms: AP CSA Review (Albert.io). Exam-focused walkthrough of standard
ArrayListalgorithms and the free-response patterns that test them. - AP Computer Science A Course at a Glance (College Board). Official topic map showing where 4.10 fits in Unit 4.
Submit Assignment
Need to update a submission later? Open the submissions dashboard.