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)
  1. Annotate the spec
    • Circle: method/class names, parameter types, return type.
    • Underline: exact conditions (e.g., “only if”, “exactly”, “in order”, “first occurrence”).
  2. List edge cases mentioned or implied
    • Empty inputs, single element, boundaries, null (only if spec allows), negative numbers, duplicates.
  3. Decide traversal strategy
    • For arrays/2D arrays: choose row-major vs column-major.
    • For ArrayList removals: choose forward w/ index control or backward loop.
  4. Write “spec-first” code
    • Start with the required header.
    • Write clear variable names aligned with the prompt.
  5. Dry-run with 2 tiny tests
    • One normal case.
    • One edge case.
Q1 Methods: implementation recipe
  1. Restate input → output in one sentence (what you compute/return).
  2. Pick a pattern
    • Counting/accumulation → accumulator variable.
    • Searching → loop + early return.
    • Building a result → String builder pattern or array/list fill.
  3. Write helper methods only if they simplify
    • Don’t over-engineer; helpers are great when reused or when logic repeats.
  4. Confirm loop bounds
    • For strings: iterate 0 to < str.length().
    • For arrays: 0 to < arr.length.

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
  1. Create instance variables
    • Use private.
    • Choose types exactly as spec.
  2. 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).
  3. Write methods one at a time
    • Update fields consistently.
    • Return exactly what spec says.
  4. 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
  1. 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.
  2. Choose direction
    • Removing items: loop backwards to avoid skipping.
    • Replacing/updating: forward is fine.
  3. Respect shifting
    • After remove(i), old i+1 becomes new i.

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
  1. 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++)
  2. Decide what each cell needs
    • Value only? neighbors? row/col totals?
  3. Boundary-check neighbors
    • Validate indices before access.
    • Or restrict loop bounds (e.g., from 1 to length-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
RuleWhen to useNotes
Use return earlySearching / “first time” conditionsPrevents extra flags and nested conditionals
Accumulator pattern (sum, count, best)Totals, averages, min/max, countsInitialize carefully (e.g., min as first element when possible)
Index loop for StringsAny char-by-char checks.charAt(i) valid for 0 <= i < s.length()
equals not == for StringsComparing text== compares references, not contents
Q2 Class: correctness rules
RuleWhen to useNotes
Fields are privateAlwaysAP expects encapsulation style
Constructor establishes valid stateAlwaysInitialize every field
Methods must preserve invariantsIf constraints existE.g., no negative inventory, max capacity
Avoid accidental shadowingParams same name as fieldsUse this.field = field; pattern if needed
Q3 ArrayList: must-know API + rules
OperationJava callNotes
Sizelist.size()Changes after add/remove
Get/setget(i), set(i, val)Valid indices: 0 to size()-1
Addadd(val) or add(i, val)Insert shifts right
Removeremove(i)Returns removed element; shifts left
Remove by valueremove(Integer.valueOf(x))Avoids calling remove(index) accidentally for ints

If the element type is Integer, list.remove(1) removes index 1, not value 1.

Q4 2D Array: structure facts
ConceptFactNotes
Row counta.lengthNumber of rows
Column count (row r)a[r].lengthAllows “ragged” arrays
Row-major traversalOuter loop rows, inner loop colsMost common FRQ expectation
Bounds check0 <= r < a.length, 0 <= c < a[r].lengthMust 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

  1. 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.
  2. Off-by-one loop bounds (Strings/arrays)

    • What goes wrong: IndexOutOfBoundsException or missing last element.
    • Fix: memorize: last valid index is length - 1 (or size() - 1). For substrings of length 2, loop to < length - 1.
  3. Using == for String comparison

    • What goes wrong: tests fail unpredictably.
    • Fix: use s.equals(t) (or s.equalsIgnoreCase(t) only if prompt says).
  4. Removing from ArrayList while iterating forward

    • What goes wrong: you skip elements because indices shift left.
    • Fix: iterate backwards or decrement i after removal (backwards is cleaner).
  5. Accidentally calling remove(index) instead of remove(value) for Integer lists

    • What goes wrong: removes the wrong element.
    • Fix: use list.remove(Integer.valueOf(x)) when you mean remove-by-value.
  6. 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.
  7. Incorrect 2D bounds checks (using a[0].length everywhere)

    • What goes wrong: fails on ragged arrays.
    • Fix: use a[r].length for the current row.
  8. Forgetting to initialize fields/collections in Q2 constructors

    • What goes wrong: NullPointerException later.
    • Fix: every field gets a value in the constructor (numbers/booleans, objects via new).

Memory Aids & Quick Tricks

Trick / mnemonicHelps you rememberWhen to use
“Header first, logic second”Matching signatures is non-negotiableStart every part
“Backward to delete”Removing from ArrayList safelyAny remove in a loop
“Row length is a[r].lengthRagged-safe 2D traversalAny 2D array loop
“Shift happens”ArrayList indices change after add/removeDebugging wrong removals/skips
“Reset inside outer loop”Correct per-row/per-col totalsRow/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 .equals for Strings
    • [ ] Early return when prompt wants first match / boolean existence
  • Q2 Class

    • [ ] All fields are private and initialized in constructor(s)
    • [ ] Methods update state exactly as described
    • [ ] Invariants enforced (if specified)
    • [ ] No shadowing/assignment mistakes (this.field when needed)
  • Q3 ArrayList

    • [ ] Use size() not length
    • [ ] If removing in loop: iterate backward or adjust index
    • [ ] Watch Integer remove ambiguity (index vs value)
    • [ ] No enhanced for-loop when modifying list
  • Q4 2D Array

    • [ ] Outer loop rows (a.length), inner loop cols (a[r].length)
    • [ ] Accumulators reset per row/col
    • [ ] Neighbor access always boundary-checked

You’ve got this—execute the spec, avoid the classic traps, and keep your code simple and readable.