FRQ Strategy Guide for 2026: Q1 Methods, Q2 Class, Q3 ArrayList, Q4 2D Array
What You Need to Know
AP CSA FRQs are predictable by question type. Each question rewards clean, correct Java that matches the spec more than “clever” code. Your job is to:
- Follow the method/class headers exactly (names, params, return types).
- Implement the stated behavior using the allowed tools (loops, conditionals, arrays/ArrayLists, simple helpers).
- Protect against edge cases explicitly mentioned (empty lists, bounds, etc.).
The 2026-style FRQ pattern you should expect
- Q1: Methods — Write one or more standalone methods. Heavy on loops, conditionals, Strings, math, and helper methods.
- Q2: Class — Implement a class from a spec: instance variables, constructor(s), methods, sometimes toString. Must maintain invariants.
- Q3: ArrayList — Manipulate/compute from an ArrayList. Index shifting and removal rules are the usual traps.
- Q4: 2D Array — Traverse grids. You must control row/col loops and boundary checks.
Critical reminder: You get full credit for correctness and matching the specification, not for minimal lines of code.
Step-by-Step Breakdown
Use this same workflow on every FRQ part.
Universal FRQ workflow (do this every time)
- Annotate the spec
- Circle: method/class names, parameter types, return type.
- Underline: exact conditions (e.g., “only if”, “exactly”, “in order”, “first occurrence”).
- List edge cases mentioned or implied
- Empty inputs, single element, boundaries, null (only if spec allows), negative numbers, duplicates.
- Decide traversal strategy
- For arrays/2D arrays: choose row-major vs column-major.
- For ArrayList removals: choose forward w/ index control or backward loop.
- Write “spec-first” code
- Start with the required header.
- Write clear variable names aligned with the prompt.
- Dry-run with 2 tiny tests
- One normal case.
- One edge case.
Q1 Methods: implementation recipe
- Restate input → output in one sentence (what you compute/return).
- Pick a pattern
- Counting/accumulation → accumulator variable.
- Searching → loop + early return.
- Building a result → String builder pattern or array/list fill.
- Write helper methods only if they simplify
- Don’t over-engineer; helpers are great when reused or when logic repeats.
- Confirm loop bounds
- For strings: iterate
0to< str.length(). - For arrays:
0to< arr.length.
- For strings: iterate
Micro-example (annotated):
// Return true if s contains at least 2 digits.
public static boolean hasTwoDigits(String s) {
int count = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c >= '0' && c <= '9') {
count++;
if (count >= 2) return true; // early exit
}
}
return false;
}
Q2 Class: spec-to-code pipeline
- Create instance variables
- Use
private. - Choose types exactly as spec.
- Use
- Implement constructor(s)
- Assign parameters to fields.
- Initialize collections/arrays (don’t forget
new). - Enforce invariants if specified (e.g., clamp values, compute derived fields).
- Write methods one at a time
- Update fields consistently.
- Return exactly what spec says.
- Check aliasing rules
- If spec implies “store a copy” vs “store reference”, be careful (often they don’t require deep copy unless stated).
Micro-example (pattern):
public class ScoreKeeper {
private int total;
private int count;
public ScoreKeeper() {
total = 0;
count = 0;
}
public void addScore(int score) {
total += score;
count++;
}
public double getAverage() {
if (count == 0) return 0.0;
return (double) total / count;
}
}
Q3 ArrayList: safe traversal + modification plan
- Identify if you modify the list
- If no modification: enhanced for-loop is fine.
- If remove/add while iterating: usually use an index-based loop.
- Choose direction
- Removing items: loop backwards to avoid skipping.
- Replacing/updating: forward is fine.
- Respect shifting
- After
remove(i), oldi+1becomes newi.
- After
Micro-example (remove safely):
// Remove all negative integers from list.
public static void removeNegatives(ArrayList<Integer> list) {
for (int i = list.size() - 1; i >= 0; i--) {
if (list.get(i) < 0) list.remove(i);
}
}
Q4 2D Array: traversal + boundary checks
- Write loop skeleton first
- Row-major is default:
for (int r = 0; r < grid.length; r++)for (int c = 0; c < grid[r].length; c++)
- Row-major is default:
- Decide what each cell needs
- Value only? neighbors? row/col totals?
- Boundary-check neighbors
- Validate indices before access.
- Or restrict loop bounds (e.g., from
1tolength-2) if you only handle interior.
Micro-example (neighbor sum with checks):
public static int neighborSum(int[][] a, int r, int c) {
int sum = 0;
int[][] dirs = { {-1,0}, {1,0}, {0,-1}, {0,1} };
for (int i = 0; i < dirs.length; i++) {
int nr = r + dirs[i][0];
int nc = c + dirs[i][1];
if (nr >= 0 && nr < a.length && nc >= 0 && nc < a[nr].length) {
sum += a[nr][nc];
}
}
return sum;
}
Key Formulas, Rules & Facts
Q1 Methods: high-frequency rules
| Rule | When to use | Notes |
|---|---|---|
Use return early | Searching / “first time” conditions | Prevents extra flags and nested conditionals |
Accumulator pattern (sum, count, best) | Totals, averages, min/max, counts | Initialize carefully (e.g., min as first element when possible) |
| Index loop for Strings | Any char-by-char check | s.charAt(i) valid for 0 <= i < s.length() |
equals not == for Strings | Comparing text | == compares references, not contents |
Q2 Class: correctness rules
| Rule | When to use | Notes |
|---|---|---|
Fields are private | Always | AP expects encapsulation style |
| Constructor establishes valid state | Always | Initialize every field |
| Methods must preserve invariants | If constraints exist | E.g., no negative inventory, max capacity |
| Avoid accidental shadowing | Params same name as fields | Use this.field = field; pattern if needed |
Q3 ArrayList: must-know API + rules
| Operation | Java call | Notes |
|---|---|---|
| Size | list.size() | Changes after add/remove |
| Get/set | get(i), set(i, val) | Valid indices: 0 to size()-1 |
| Add | add(val) or add(i, val) | Insert shifts right |
| Remove | remove(i) | Returns removed element; shifts left |
| Remove by value | remove(Integer.valueOf(x)) | Avoids calling remove(index) accidentally for ints |
If the element type is
Integer,list.remove(1)removes index1, not value1.
Q4 2D Array: structure facts
| Concept | Fact | Notes |
|---|---|---|
| Row count | a.length | Number of rows |
| Column count (row r) | a[r].length | Allows “ragged” arrays |
| Row-major traversal | Outer loop rows, inner loop cols | Most common FRQ expectation |
| Bounds check | 0 <= r < a.length, 0 <= c < a[r].length | Must use a[r].length for current row |
Examples & Applications
Example 1 (Q1 Methods): “compute + condition”
Prompt style: Return the number of times a pattern occurs, but only count occurrences that meet a condition.
Key insight: Use one pass + careful bounds.
// Count occurrences of "hi" in s, not overlapping.
public static int countHi(String s) {
int count = 0;
for (int i = 0; i < s.length() - 1; i++) {
if (s.substring(i, i + 2).equals("hi")) {
count++;
i++; // skip next char to prevent overlap
}
}
return count;
}
Variation to watch: If prompt says “overlapping allowed,” remove the i++.
Example 2 (Q2 Class): “state changes + derived result”
Prompt style: Implement methods that update fields and report something.
Key insight: Keep state consistent; handle edge cases (like division by zero).
public class TripMeter {
private int miles;
public TripMeter() { miles = 0; }
public void drive(int d) {
if (d > 0) miles += d;
}
public int getMiles() { return miles; }
}
Variation to watch: If prompt specifies exact behavior for invalid input (negative), follow it strictly.
Example 3 (Q3 ArrayList): “remove while scanning”
Prompt style: Remove all elements that match a predicate; return how many removed.
Key insight: Go backwards to avoid skipping.
public static int removeMultiples(ArrayList<Integer> list, int k) {
int removed = 0;
for (int i = list.size() - 1; i >= 0; i--) {
if (list.get(i) % k == 0) {
list.remove(i);
removed++;
}
}
return removed;
}
Variation to watch: If they want you to keep order, backward removal preserves order of remaining elements.
Example 4 (Q4 2D Array): “patterned traversal (row/col totals)”
Prompt style: Find the row index with the greatest sum.
Key insight: Reset sum each row; track best.
public static int maxRowIndex(int[][] a) {
int bestRow = 0;
int bestSum = 0;
for (int r = 0; r < a.length; r++) {
int sum = 0;
for (int c = 0; c < a[r].length; c++) sum += a[r][c];
if (r == 0 || sum > bestSum) {
bestSum = sum;
bestRow = r;
}
}
return bestRow;
}
Variation to watch: Ragged arrays: always use a[r].length.
Common Mistakes & Traps
Wrong header (name/params/return type)
- What goes wrong: your logic can be right but won’t match the required signature.
- Fix: copy headers exactly from the prompt before coding.
Off-by-one loop bounds (Strings/arrays)
- What goes wrong:
IndexOutOfBoundsExceptionor missing last element. - Fix: memorize: last valid index is
length - 1(orsize() - 1). For substrings of length 2, loop to< length - 1.
- What goes wrong:
Using
==for String comparison- What goes wrong: tests fail unpredictably.
- Fix: use
s.equals(t)(ors.equalsIgnoreCase(t)only if prompt says).
Removing from ArrayList while iterating forward
- What goes wrong: you skip elements because indices shift left.
- Fix: iterate backwards or decrement
iafter removal (backwards is cleaner).
Accidentally calling
remove(index)instead ofremove(value)forIntegerlists- What goes wrong: removes the wrong element.
- Fix: use
list.remove(Integer.valueOf(x))when you mean remove-by-value.
Not resetting per-row/per-col accumulators in 2D arrays
- What goes wrong: sums blend across rows/cols.
- Fix: declare/reset
sum = 0;inside the outer loop.
Incorrect 2D bounds checks (using
a[0].lengtheverywhere)- What goes wrong: fails on ragged arrays.
- Fix: use
a[r].lengthfor the current row.
Forgetting to initialize fields/collections in Q2 constructors
- What goes wrong:
NullPointerExceptionlater. - Fix: every field gets a value in the constructor (numbers/booleans, objects via
new).
- What goes wrong:
Memory Aids & Quick Tricks
| Trick / mnemonic | Helps you remember | When to use |
|---|---|---|
| “Header first, logic second” | Matching signatures is non-negotiable | Start every part |
| “Backward to delete” | Removing from ArrayList safely | Any remove in a loop |
“Row length is a[r].length” | Ragged-safe 2D traversal | Any 2D array loop |
| “Shift happens” | ArrayList indices change after add/remove | Debugging wrong removals/skips |
| “Reset inside outer loop” | Correct per-row/per-col totals | Row/col sum/max problems |
| “Early return for first match” | Simplifies search problems | “Find first”, “exists”, “stop when” |
Quick Review Checklist
Q1 Methods
- [ ] Signature copied exactly; return on all paths
- [ ] Loop bounds correct (especially substring/neighbor-like logic)
- [ ] Use
.equalsfor Strings - [ ] Early return when prompt wants first match / boolean existence
Q2 Class
- [ ] All fields are
privateand initialized in constructor(s) - [ ] Methods update state exactly as described
- [ ] Invariants enforced (if specified)
- [ ] No shadowing/assignment mistakes (
this.fieldwhen needed)
- [ ] All fields are
Q3 ArrayList
- [ ] Use
size()notlength - [ ] If removing in loop: iterate backward or adjust index
- [ ] Watch
Integerremove ambiguity (index vs value) - [ ] No enhanced for-loop when modifying list
- [ ] Use
Q4 2D Array
- [ ] Outer loop rows (
a.length), inner loop cols (a[r].length) - [ ] Accumulators reset per row/col
- [ ] Neighbor access always boundary-checked
- [ ] Outer loop rows (
You’ve got this—execute the spec, avoid the classic traps, and keep your code simple and readable.