The AP CSA exam is half free-response — four problems, 90 minutes, 36 points total. Practice all four types here, write your code in the built-in IDE, then check yourself against the official rubric style.
| FRQ | Type | Typically Tests | Points |
|---|---|---|---|
| 1 | Methods | Method writing, control flow, Strings | 9 |
| 2 | Class | Designing & writing a class | 9 |
| 3 | Array/ArrayList | 1D collection algorithms | 9 |
| 4 | 2D Array | 2D array traversal | 9 |
Total: 90 minutes for 36 points. That's about 22 minutes per question. Don't spend more.
Spend 5 minutes reading and 17 writing per FRQ. Don't get stuck — skip ahead and come back. Partial credit is real.
Your code must compile to earn full credit. Curly braces, semicolons, return types, parameter types — all required.
Copy the method header from the prompt exactly. Don't invent your own parameter names — they should match what the problem gave you.
If the problem gives you a method to use (an accessor, a helper), USE IT. Re-implementing the helper costs you a rubric point.
The rubric is based on behavior, not style. As long as your code does the right thing for the listed test cases, you get the points. Elegant code with a bug scores worse than ugly code that works.
A code key is a string of letters used to encode a message. The StringHider class has a static method hide that takes a String message and replaces every letter that also appears in a given key with the character '*'. All other characters (digits, spaces, punctuation, letters not in the key) remain unchanged. Comparison is case-sensitive.
Write the method hide with the following signature:
public static String hide(String message, String key)
Examples:
| Call | Returns |
|---|---|
hide("Hello World", "lo") | "He*** W*r*d" |
hide("Java rocks!", "aeiou") | "J*v* r*cks!" |
hide("abc 123", "xyz") | "abc 123" |
Write your attempt below, then run it. The main method calls the test cases.
message using a loop+2substring(i, i+1))+1indexOf or substring search to check key membership+2"*" when the character is in the key+1Strategy: Build the answer one character at a time. For each character, check if it's in the key — if yes, append *; if not, append the character itself.
public static String hide(String message, String key) {
String result = "";
for (int i = 0; i < message.length(); i++) {
String ch = message.substring(i, i + 1);
if (key.indexOf(ch) >= 0) {
result += "*";
} else {
result += ch;
}
}
return result;
}
Why this scores 9/9: Walks every char, uses indexOf for key check (returns –1 when not found), builds result correctly, handles edge cases (empty key works too).
Write a static method digitSum(int n) that returns the sum of all the digits in a non-negative integer n. Example: digitSum(1729) = 1+7+2+9 = 19. Precondition: n >= 0.
Then write a static method isDigitMultiple(int n) that returns true if and only if the digit sum of n divides n evenly. Use your digitSum helper. Example: isDigitMultiple(18) is true because digit sum is 9 and 18 / 9 = 2 with no remainder.
digitSum: handles n = 0 correctly (returns 0)+1digitSum: loop continues while there are digits left+1digitSum: extracts last digit with n % 10+1digitSum: removes last digit with n / 10+1digitSum: accumulates and returns sum+2isDigitMultiple: calls digitSum rather than re-implementing+1isDigitMultiple: uses % to check divisibility correctly+1isDigitMultiple: handles the edge case where digit sum is 0 (n must be 0)+1public static int digitSum(int n) {
if (n == 0) return 0;
int sum = 0;
while (n > 0) {
sum += n % 10;
n = n / 10;
}
return sum;
}
public static boolean isDigitMultiple(int n) {
int s = digitSum(n);
if (s == 0) return n == 0; // avoid divide by zero
return n % s == 0;
}
A school assigns lockers to students. Implement a Locker class with these specifications:
getNumber(), getOwner().assign(String name) sets the owner if the locker is currently unassigned (owner is the empty String). Returns true on success, false if already assigned.release() clears the owner (sets to empty String). No return value.isAvailable() returns true if the locker has no owner.private instance variables: number (int) and owner (String)+2getNumber and getOwner return the right fields+1isAvailable returns whether owner is empty (uses .equals()!)+1assign checks availability before assigning+1assign returns true on success, false on failure+1assign actually updates the owner field+1release clears owner to empty String+1class Locker {
private int number;
private String owner;
public Locker(int number) {
this.number = number;
owner = "";
}
public int getNumber() { return number; }
public String getOwner() { return owner; }
public boolean isAvailable() { return owner.equals(""); }
public boolean assign(String name) {
if (!isAvailable()) return false;
owner = name;
return true;
}
public void release() { owner = ""; }
}
Common mistakes: Using == instead of .equals() for the empty String check, forgetting private on instance variables, having assign always update (even when the locker is taken).
Given an ArrayList<Integer> of test scores, write two static methods:
countPassing(ArrayList<Integer> scores, int min) returns the count of scores ≥ min.removeFailing(ArrayList<Integer> scores, int min) removes every score below min from the list in place. Returns nothing.countPassing: loop traverses entire list+1countPassing: condition >= min (not just >)+1countPassing: increments counter and returns it+2removeFailing: iterates correctly without skipping elements after removal+2removeFailing: uses list.remove(i) to modify in place+1removeFailing: condition is "below min" (i.e., < min)+1public static int countPassing(ArrayList<Integer> scores, int min) {
int count = 0;
for (int s : scores) {
if (s >= min) count++;
}
return count;
}
public static void removeFailing(ArrayList<Integer> scores, int min) {
// Iterate BACKWARDS to avoid index-shifting bugs
for (int i = scores.size() - 1; i >= 0; i--) {
if (scores.get(i) < min) scores.remove(i);
}
}
Common bug: Iterating forward with remove. After you remove index i, the next element shifts into i — but your loop increments to i+1, skipping it. Iterate backwards.
A classroom is modeled as a rectangular 2D array of String where each cell contains a student name or "" for an empty seat. Write three static methods:
countEmpty(String[][] room) returns the number of empty seats.rowWithMostStudents(String[][] room) returns the index of the row with the most occupied seats. If there's a tie, return the smallest index.compactRow(String[][] room, int r) rearranges row r so all occupied seats come first, in their original order, and all empty seats follow.countEmpty: nested loop over rows and columns+1countEmpty: correctly tests for empty using .equals("")+1countEmpty: increments and returns the count+1rowWithMostStudents: counts occupied per row+1rowWithMostStudents: tracks best count and best row, returns it+1rowWithMostStudents: tie-breaks toward smallest index (uses >, not >=)+1compactRow: gathers non-empty names in order+1compactRow: writes them back to the start of row r+1compactRow: fills the rest of row r with ""+1public static int countEmpty(String[][] room) {
int count = 0;
for (int r = 0; r < room.length; r++) {
for (int c = 0; c < room[r].length; c++) {
if (room[r][c].equals("")) count++;
}
}
return count;
}
public static int rowWithMostStudents(String[][] room) {
int bestRow = 0, bestCount = -1;
for (int r = 0; r < room.length; r++) {
int count = 0;
for (int c = 0; c < room[r].length; c++) {
if (!room[r][c].equals("")) count++;
}
if (count > bestCount) { // strictly greater → ties go to smaller index
bestCount = count;
bestRow = r;
}
}
return bestRow;
}
public static void compactRow(String[][] room, int r) {
int writeIdx = 0;
String[] row = room[r];
// Pass 1: collect non-empties at the start
for (int c = 0; c < row.length; c++) {
if (!row[c].equals("")) {
row[writeIdx++] = row[c];
}
}
// Pass 2: fill the rest with ""
while (writeIdx < row.length) row[writeIdx++] = "";
}
The two-pointer trick: A write index tracks where to put the next non-empty. After scanning, fill the tail with empties. This is the standard "compact" pattern.
The College Board publishes every past FRQ with scoring guidelines.
Past Exam Questions (College Board)