CompSci.rocks
FRQcsapa

CookieOrder / MasterOrder: 2010 FRQ 1

A step-by-step solution to the 2010 AP CSA FRQ 1 (CookieOrder / MasterOrder), covering summing values across an ArrayList and safely removing matching elements while iterating in Java.

Selling boxes of cookies for a fundraiser is the backdrop for this AP Computer Science A free-response question, and it hands you a list of custom objects to work with instead of a plain array — first totaling a value across every element, then safely removing some of them while you're still in the middle of scanning the list.

What This FRQ Tests

  • AP CSA units: Unit 7 (ArrayList) and Unit 4 (Iteration)
  • Core skill: traversing a List of custom objects to accumulate a numeric total
  • Secondary skill: removing matching elements from an ArrayList mid-traversal without accidentally skipping any
  • Official category: content-wise, this is an "Array/ArrayList" question — but 2010 predates the standardized FRQ-number-to-category order that AP CSA settled into starting with the 2019–2020 Course and Exam Description redesign. The exam booklet itself prints this question as FRQ 1, so that's the number used here, even though a modern exam would slot ArrayList-based work into the FRQ 3 position instead.

The Setup

  • The given CookieOrder class (not modified) provides:
    • A constructor: CookieOrder(String variety, int numBoxes)
    • String getVariety() — the cookie variety for this order
    • int getNumBoxes() — how many boxes were ordered
  • MasterOrder holds:
    • private List<CookieOrder> orders — built as new ArrayList<CookieOrder>() in the constructor
    • void addOrder(CookieOrder theOrder) — already written; appends to orders
  • You're asked to write two methods:
    • getTotalBoxes() — the sum of getNumBoxes() across every order in the list
    • removeVariety(String cookieVar) — removes every order matching cookieVar and returns the total boxes removed

Part (a): Writing getTotalBoxes()

Step-by-Step Approach

  1. Start a running total at 0.
  2. Loop over every index of orders.
  3. On each iteration, pull out that CookieOrder and add its box count to the total.
  4. After the loop, return the total.

The Code

public int getTotalBoxes()
{
    int total = 0;

    for (int i = 0; i < orders.size(); i++)
    {
        total += orders.get(i).getNumBoxes();
    }

    return total;
}

Why Each Piece Matters

  • orders.get(i).getNumBoxes()get(i) returns the CookieOrder object at that position; you then call getNumBoxes() on that object to get the number you actually want to add.
  • Starting total at 0 — this automatically handles the "no cookie orders" case correctly. If orders is empty, the loop body never runs, and 0 is returned as-is — no special-case if needed.
  • orders.size(), not a hardcoded number — the list can hold any number of orders, so the loop bound has to come from the list itself.

Common Mistakes to Avoid

  • Adding the CookieOrder object itself instead of getNumBoxes(). total += orders.get(i); doesn't even compile — you must go through the getter to reach the int you need.
  • Off-by-one on the loop bound. Using i <= orders.size() instead of i < orders.size() calls get(...) one index past the end of the list.
  • Re-declaring total inside the loop. Declaring int total = 0; inside the loop body resets it to 0 every iteration instead of accumulating.

Part (b): Writing removeVariety(String cookieVar)

The Rule, Broken Down

  1. Every order in orders whose variety matches cookieVar gets removed from the list.
  2. Before removing an order, its box count gets added to a running total.
  3. There can be zero, one, or several matching orders — the method has to handle all of those correctly.
  4. The method returns the total boxes removed, whether that's 0 or a large number.

Step-by-Step Approach

  1. Start a running total at 0.
  2. Loop over orders, but backwards — from the last index down to 0.
  3. On each iteration, check whether that order's variety matches cookieVar.
  4. If it matches, add its box count to the total, then remove it from the list.
  5. After the loop finishes, return the total.

The Code

public int removeVariety(String cookieVar)
{
    int totalRemoved = 0;

    for (int i = orders.size() - 1; i >= 0; i--)
    {
        CookieOrder order = orders.get(i);

        if (order.getVariety().equals(cookieVar))
        {
            totalRemoved += order.getNumBoxes();
            orders.remove(i);
        }
    }

    return totalRemoved;
}

Why the Backward Loop Matters

  • Removing from an ArrayList shifts every later element one position to the left. If you're looping forward and you remove the element at index i, the element that used to be at i + 1 slides into index i — but a forward loop then increments to i + 1 next, silently skipping the element that just slid into i.
  • Looping backward avoids this entirely. When you remove index i, only elements at indices greater than i shift — and a backward loop has already finished visiting those. The remaining indices you still need to check (everything less than i) are completely untouched by the shift.
  • order.getVariety().equals(cookieVar) — always compare String contents with .equals(). Two different String objects holding the same characters aren't guaranteed to be ==.

Tracing the Example

Using the question's own code segment — goodies starts with orders ("Chocolate Chip", 1), ("Shortbread", 5), ("Macaroon", 2), ("Chocolate Chip", 3) at indices 0–3. Tracing goodies.removeVariety("Chocolate Chip") with the backward loop:

i Order at i Matches? totalRemoved List after this step
3 ("Chocolate Chip", 3) yes 3 ["Chocolate Chip" 1, "Shortbread" 5, "Macaroon" 2]
2 ("Macaroon", 2) no 3 unchanged
1 ("Shortbread", 5) no 3 unchanged
0 ("Chocolate Chip", 1) yes 4 ["Shortbread" 5, "Macaroon" 2]

The method returns 4, and the list ends up as ["Shortbread" 5, "Macaroon" 2] — both match the question exactly. A follow-up call to goodies.removeVariety("Brownie") finds no matches at any index, so totalRemoved stays 0 and the list is untouched, also matching the question.

Common Mistakes to Avoid

  • Looping forward and removing as you go. This is the single most common bug on this exact style of problem — it silently skips whichever order slides into the just-vacated index.
  • Comparing varieties with == instead of .equals(). This is one of the most frequent AP CSA point losses anywhere String comparison appears.
  • Adding the box count after removing the order, or trying to read getNumBoxes() from a reference obtained after remove(i) already ran. Grab the object with get(i) and read what you need from it before removing it.
  • Forgetting the method still needs to return 0 correctly when nothing matches — this falls out naturally as long as totalRemoved starts at 0 and the if simply never triggers.

Notes: A Simpler Removal Pattern Not on the Quick Reference Sheet

Java's Iterator interface offers a pattern that sidesteps the whole "which direction should I loop" question entirely, since it.remove() always removes whatever element it.next() most recently returned, safely:

import java.util.Iterator;

public int removeVariety(String cookieVar)
{
    int totalRemoved = 0;
    Iterator<CookieOrder> it = orders.iterator();

    while (it.hasNext())
    {
        CookieOrder order = it.next();

        if (order.getVariety().equals(cookieVar))
        {
            totalRemoved += order.getNumBoxes();
            it.remove();
        }
    }

    return totalRemoved;
}
  • The Iterator class isn't listed on the AP Quick Reference sheet at all — only ArrayList's own size/add/get/set/remove methods are. That doesn't mean using an Iterator is against the rules; it's completely valid Java and a completely valid thing to write on the real exam. The only real tradeoff is that you won't have its exact behavior printed for you to double-check mid-exam the way you would with the backward-loop version above.
  • it.remove() is specifically designed to be safe to call immediately after it.next(), which is exactly why this pattern avoids the index-shifting problem without needing to reason about loop direction at all.

Key Takeaways

  • Removing elements from an ArrayList while scanning it is a recurring AP CSA trap — loop backward (or use an Iterator), never forward with a plain index.
  • When a method needs to report "how much got removed," accumulate from each matching element before removing it, not after.
  • String fields always compare with .equals(), never ==, regardless of how the surrounding loop is structured.

Related FRQs