AP Computer Science A

Lesson 19: FRQ Practice Set 2 — Classes and Data StructuresAP

🎧 Listen to this lesson

This practice set trains the two heavyweight FRQ types: Class Design (type 2) and Array/ArrayList/2D (types 3–4). These questions reward completeness over brilliance — every specified field, constructor behaviour and method is a point, and the points are collected by writing the whole specification carefully. Two fully worked problems with self-scoring checklists first, then fresh exam questions.

Worked Problem A: a complete class (LibraryCard)

Prompt. Write the complete class LibraryCard. A library card has a private String for the holder's name and a private int for the number of books currently borrowed. The constructor takes the holder's name; a new card starts with 0 books. The class provides: borrowBook(), which increases the count by 1 but never lets it exceed the maximum of 5 (if the card is already at 5, the count does not change); returnBook(), which decreases the count by 1 but never below 0; getBooksOut(), which returns the count; and toString(), which returns a string of the form "Ana has 3 book(s) out". Approach notes. Class FRQs are transcription exercises with three traps: (1) spec constraints that live inside mutators ("never exceed 5", "never below 0") — these are guard clauses, and each is a rubric point; (2) a *maximum* stated in the prompt should become a named constant or a literal guard — either earns the point, but forgetting the guard entirely is the classic loss; (3) toString() must return, not print. Model solution.

public class LibraryCard
{
    private String holder;
    private int booksOut;

    public LibraryCard(String name)
    {
        holder = name;
        booksOut = 0;
    }

    public void borrowBook()
    {
        if (booksOut < 5)
        {
            booksOut++;
        }
    }

    public void returnBook()
    {
        if (booksOut > 0)
        {
            booksOut--;
        }
    }

    public int getBooksOut()
    {
        return booksOut;
    }

    public String toString()
    {
        return holder + " has " + booksOut + " book(s) out";
    }
}

Self-scoring checklist. Fields private and correctly typed? (1) Constructor sets the name and initialises the count to 0? (2) borrowBook increments with the ceiling guard at 5? (2) returnBook decrements with the floor guard at 0? (2) Accessor returns the count? (1) toString returns the exact specified format? (1)

Worked Problem B: 2D transformation (transpose)

Prompt. Write the static method transpose, which takes a rectangular int[][] and returns a new 2D array that is the transpose of the original: the element at row r, column c of the original appears at row c, column r of the result. The original array must not be modified. For example, transposing {{1, 2, 3}, {4, 5, 6}} (2 rows × 3 columns) returns {{1, 4}, {2, 5}, {3, 6}} (3 rows × 2 columns). Approach notes. Two disciplines are being tested. First, dimensions flip: a result for an R×C input is C×R — allocating new int[grid[0].length][grid.length] is the point students miss by allocating the same shape as the input. Second, the original is untouched: build a fresh array and fill it; any solution that swaps elements in place corrupts data (and only works for square arrays anyway). The assignment itself is one line — result[c][r] = grid[r][c] — with the index order doing all the work. Model solution.

public static int[][] transpose(int[][] grid)
{
    int[][] result = new int[grid[0].length][grid.length];
    for (int r = 0; r < grid.length; r++)
    {
        for (int c = 0; c < grid[r].length; c++)
        {
            result[c][r] = grid[r][c];
        }
    }
    return result;
}

Self-scoring checklist. Header returns int[][]? (1) New array allocated with flipped dimensions? (2) Every element of the original visited? (1) Each element written to the swapped position [c][r]? (2) Original array never assigned into? (1) Result returned? (2)

What distinguishes these two problem types

Class questions test completeness against a prose spec — read the prompt as a checklist and tick items off in your code. Data-structure questions test index and state discipline — bounds, dimensions, which structure changes and which must not. In both, the Lesson 15 protocol applies verbatim: headers first, simplest correct approach, then a hand-trace of one normal case and one edge case (for the class: borrow a 6th book; for transpose: a non-square array).

Common mistakes specific to these types

  • Public fields in class FRQs (forfeits the encapsulation point instantly).
  • Guards on the wrong side: checking booksOut > 0 in borrowBook — read which direction each mutator moves the value and what limit it approaches.
  • Returning a modified input when the prompt demanded a *new* structure (or vice versa: building a copy when the prompt said "modifies the given array" — that phrasing decides whether void + mutation or a return value is expected).
  • Square-array assumptions: allocating or traversing a 2D structure as if rows always equal columns.
Try it. Try it: trace the transpose solution on {{7}, {8}, {9}} (3 rows × 1 column). (Result is 1 row × 3 columns: {{7, 8, 9}} — dimension flip confirmed.)

AP Exam Experience

Question 1. Consider the following class and code segment.

public class Counter
{
    private int value;
    public Counter() { value = 0; }
    public void bump() { value++; }
    public int getValue() { return value; }
}

Counter x = new Counter();
Counter y = x;
x.bump();
y.bump();
x.bump();
System.out.println(x.getValue() + " " + y.getValue());

What is printed?

  • (A) 3 2
  • (B) 2 3
  • (C) 3 3
  • (D) 2 2
  • (E) Nothing; the code does not compile.

Answer: (C). y = x copies the reference — one object, three bump() calls through two names, both reads report 3. The class-practice version of the aliasing rule from Lesson 5.

Question 2. Consider the following code segment.

ArrayList<Integer> data = new ArrayList<Integer>();
data.add(10);
data.add(20);
data.add(30);
data.set(1, 99);
data.remove(0);
System.out.println(data.size() + " " + data.get(0));

What is printed?

  • (A) 3 99
  • (B) 2 99
  • (C) 2 20
  • (D) 3 20
  • (E) 2 30

Answer: (B). After the adds: [10, 20, 30]. set(1, 99) replaces in place: [10, 99, 30]. remove(0) drops the 10 and shifts: [99, 30]. Size 2, first element 99. Confusing set with add(index, ...) (which inserts and grows the list) is the designed trap.

Question 3. Consider the following code segment.

int[][] m = {{1, 2, 3}, {4, 5}};
int sum = 0;
for (int r = 0; r < m.length; r++)
{
    for (int c = 0; c < m[r].length; c++)
    {
        sum += m[r][c];
    }
}
System.out.println(sum + " " + m.length + " " + m[1].length);

What is printed?

  • (A) 15 2 3
  • (B) 15 3 2
  • (C) 15 2 2
  • (D) 12 2 2
  • (E) Nothing; an exception is thrown.

Answer: (C). The array is ragged: row 0 has 3 elements, row 1 has 2. The traversal uses m[r].length, so it sums all five values: 15. m.length is 2 (rows) and m[1].length is 2. (A) assumes a rectangular 2×3; (E) is what happens to code that assumes it too.

Free-Response Question. Write the complete class Course, representing a school course with an enrolment list, with the following specification:

  • A private String instance variable for the course title, and a private ArrayList<String> instance variable holding the names of enrolled students.
  • A constructor that takes the course title and initialises the enrolment list to an empty list.
  • A method enroll(String name) that adds name to the list only if that name is not already enrolled (no duplicates).
  • A method drop(String name) that removes name from the list if it is enrolled, and does nothing otherwise.
  • A method getClassSize() that returns the number of enrolled students.
  • A method isEnrolled(String name) that returns true if name is in the list and false otherwise.

Scoring guideline (9 points).

  • +1 Both instance variables declared private with correct types (String, ArrayList<String>)
  • +1 Constructor stores the title
  • +1 Constructor initialises the list to a new empty ArrayList (not left as null)
  • +1 enroll checks whether the name is already present before adding (uses isEnrolled, contains-style logic via a loop or indexOf — any correct membership test)
  • +1 enroll adds the name when it is absent
  • +1 drop removes the name when present and makes no change (and throws no error) when absent
  • +1 getClassSize() returns the list's size()
  • +1 isEnrolled correctly reports membership for both present and absent names
  • +1 Class compiles as a complete, self-contained class (with the ArrayList import handled as in the exam's provided framework)

Model solution.

import java.util.ArrayList;

public class Course
{
    private String title;
    private ArrayList<String> roster;

    public Course(String t)
    {
        title = t;
        roster = new ArrayList<String>();
    }

    public void enroll(String name)
    {
        if (!isEnrolled(name))
        {
            roster.add(name);
        }
    }

    public void drop(String name)
    {
        for (int i = 0; i < roster.size(); i++)
        {
            if (roster.get(i).equals(name))
            {
                roster.remove(i);
                return;
            }
        }
    }

    public int getClassSize()
    {
        return roster.size();
    }

    public boolean isEnrolled(String name)
    {
        for (int i = 0; i < roster.size(); i++)
        {
            if (roster.get(i).equals(name))
            {
                return true;
            }
        }
        return false;
    }
}


## Worked Problem C: filtering objects into a new list
**Prompt.** The class `Student` (provided) has methods `getName()` returning a `String` and `getGpa()` returning a `double`. Write the static method `honorRoll`, which takes an `ArrayList<Student>` and returns a **new** `ArrayList<String>` containing the names of all students whose GPA is **3.5 or higher**, in the order they appear in the original list. The original list must not be modified.
**Approach notes.** "Return a new list" questions test three things at once: allocating the result before the loop (forgetting this is the most common failure), using the **provided accessor methods** rather than imagining fields (Lesson 15's rule — `Student`'s fields are private and unreachable), and the boundary phrase "3.5 or higher" → `>= 3.5`. The original list is only read, so an enhanced for loop is legal and idiomatic here — a deliberate contrast with removal problems.
**Model solution.**

public static ArrayList<String> honorRoll(ArrayList<Student> students) { ArrayList<String> result = new ArrayList<String>(); for (int i = 0; i < students.size(); i++) { if (students.get(i).getGpa() >= 3.5) { result.add(students.get(i).getName()); } } return result; }

**Self-scoring checklist.** Header returns `ArrayList<String>` and takes `ArrayList<Student>`? (1) New result list created before traversal? (1) Every student examined via `get` (or enhanced loop)? (1) GPA obtained by calling the provided `getGpa()`? (1) Test is `>= 3.5` (inclusive, as specified)? (2) Name (via `getName()`) added — not the Student object? (2) Result returned; original list untouched? (1)

📊 Interactive

Interactive — hover or explore the chart above.

Keep reading to complete this lesson