CompSci.rocks
FRQcsapa

Customer: 2006 FRQ 3

A step-by-step solution to the 2006 AP CSA FRQ 3 (Customer), covering writing a tie-breaking comparison method and merging two sorted arrays into a fixed-size result array without duplicates in Java.

Putting customers in a well-defined order — and then merging two already-sorted customer lists together — is the two-part challenge in this AP Computer Science A free-response question.

What This FRQ Tests

  • AP CSA units: Unit 5 (Writing Classes) and Unit 6 (Array)
  • Core skill: writing a comparison method that orders objects by one field first, and only falls back to a second field when the first is a tie
  • Secondary skill: merging two sorted arrays into a third, fixed-size array using a mergesort-style algorithm, without creating any extra data structures
  • Official category: "Array/ArrayList" — officially printed as FRQ 3 on the 2006 exam. Part (a) is really a Classes-flavored comparison method, but the harder, defining piece of this question is the array-merging algorithm in part (b).

The Setup

  • The given (incomplete) Customer class has:
    • A constructor: Customer(String name, int idNum)
    • String getName() and int getID() — getters
    • int compareCustomer(Customer other) — to be written in part (a); positive if this is "greater," negative if "less," 0 if equal
  • Customers are ordered alphabetically by name, using ties on ID number to break exact name matches.
  • Part (b) is a static method, prefixMerge, that fills a fixed-size Customer[] array by merging the beginnings of two other sorted Customer[] arrays, skipping duplicate customers that appear in both.

Part (a): Writing compareCustomer(Customer other)

The Rule, Broken Down

  1. Compare names first, using String's compareTo method.
  2. If the names aren't identical, that comparison alone decides the result — return it as-is.
  3. If the names are identical, break the tie using ID number: a higher ID means "greater," a lower ID means "less."

Step-by-Step Approach

  1. Compare this customer's name to other's name with getName().compareTo(other.getName()), and store the result.
  2. If that result isn't 0, the names differ — return it directly, since compareTo already returns a positive/negative/zero value in exactly the shape this method needs.
  3. If that result is 0, the names are identical — return the difference between the two ID numbers instead.

The Code

public int compareCustomer(Customer other)
{
    int nameComparison = getName().compareTo(other.getName());

    if (nameComparison != 0)
    {
        return nameComparison;
    }

    return getID() - other.getID();
}

Why Each Piece Matters

  • getName().compareTo(other.getName()), not getName() == other.getName()compareTo gives an ordering (which name comes first alphabetically), not just a yes/no equality check, and == on Strings compares object identity rather than the text itself anyway.
  • Returning nameComparison directly when it's nonzerocompareTo already returns a positive, negative, or zero value in exactly the format compareCustomer is supposed to return, so there's no need to convert it into 1, -1, or anything else.
  • getID() - other.getID(), only reached when names tie — since IDs are guaranteed positive integers, a simple subtraction gives a positive result when this has the higher ID, negative when it has the lower one, and 0 only when both fields are identical.

Tracing the Example

Using the exact objects from the prompt: c1 = Customer("Smith", 1001), c2 = Customer("Anderson", 1002), c3 = Customer("Smith", 1003).

Call Name comparison Result
c1.compareCustomer(c1) "Smith".compareTo("Smith") = 0 → falls through to IDs: 1001 - 1001 0
c1.compareCustomer(c2) "Smith".compareTo("Anderson") → positive ("Smith" comes after "Anderson" alphabetically) a positive integer
c1.compareCustomer(c3) "Smith".compareTo("Smith") = 0 → falls through to IDs: 1001 - 1003 = -2 a negative integer

All three results match the problem's table exactly.

Common Mistakes to Avoid

  • Comparing names with == instead of .equals()/.compareTo(). This is one of the most common AP CSA point losses anywhere String comparison shows up.
  • Skipping the name check and comparing IDs first. The rule is explicit that name is the primary sort key — ID only matters as a tiebreaker.
  • Trying to convert compareTo's result into a fixed -1/0/1. The method only needs the sign to be correct — compareTo and the ID subtraction both already return exactly that.

Part (b): Writing prefixMerge(Customer[] list1, Customer[] list2, Customer[] result)

The Rule, Broken Down

  1. list1 and list2 are already sorted in ascending order; result is empty (null in every slot) and is guaranteed no longer than either input array.
  2. Fill result by repeatedly taking whichever of list1's or list2's next unused customer comes first in the ordering.
  3. If the next customer in list1 and the next customer in list2 are the same customer (compareCustomer returns 0), only one copy goes into result — but both lists' positions still advance.
  4. Stop as soon as result is completely filled — this is a "prefix" merge, not a merge all the way to the end of either list.
  5. No additional array, ArrayList, or other multi-object data structure may be created as scratch space.

Step-by-Step Approach

  1. Track three separate index variables: one into list1, one into list2, and one into result.
  2. Loop until the result index reaches result.length.
  3. Each iteration, compare list1's current customer to list2's current customer with compareCustomer.
  4. If list1's customer comes first, copy it into result and advance only the list1 index.
  5. If list2's customer comes first, copy it into result and advance only the list2 index.
  6. If they're the same customer, copy just one copy into result, but advance both the list1 and list2 indices — this is what prevents the duplicate from appearing twice.
  7. Either way, advance the result index every iteration.

The Code

public static void prefixMerge(Customer[] list1, Customer[] list2, Customer[] result)
{
    int i = 0;
    int j = 0;

    for (int k = 0; k < result.length; k++)
    {
        int comparison = list1[i].compareCustomer(list2[j]);

        if (comparison < 0)
        {
            result[k] = list1[i];
            i++;
        }
        else if (comparison > 0)
        {
            result[k] = list2[j];
            j++;
        }
        else
        {
            result[k] = list1[i];
            i++;
            j++;
        }
    }
}

Why Each Piece Matters

  • Three separate index variables (i, j, k) — each one can advance a different amount from iteration to iteration (in particular, i and j both advance together only on a tie), so a single shared index wouldn't work.
  • The loop is bounded by k < result.length, not by list1.length or list2.length — this is what makes it a prefix merge. result is guaranteed to be no longer than either input array, so the loop always has enough customers available in both lists to finish safely.
  • The else branch (tie) advances both i and j — this is the one piece that's easy to miss, and it's exactly what "customers who appear in both lists will appear at most once in result" requires. Skipping it and only advancing one of the two indices would make that same duplicate customer eligible to be picked again on a later iteration.
  • No extra array or ArrayList is created — the merge reads directly from list1 and list2 and writes directly into result, exactly as the problem requires ("solutions that create any additional data structures holding multiple objects... will not receive full credit").

Tracing the Example

Using the exact arrays from the prompt (name and ID shown together, result.length is 6):

list1: Arthur 4920, Burton 3911, Burton 4944, Franz 1692, Horton 9221, Jones 5554, Miller 9360, Nguyen 4339 list2: Aaron 1729, Baker 2921, Burton 3911, Dillard 6552, Jones 5554, Miller 9360, Noble 3335

k list1[i] list2[j] Comparison Action result[k]
0 Arthur 4920 Aaron 1729 list1 name is greater copy list2[j], j++ Aaron 1729
1 Arthur 4920 Baker 2921 list1 name is less copy list1[i], i++ Arthur 4920
2 Burton 3911 Baker 2921 list1 name is greater copy list2[j], j++ Baker 2921
3 Burton 3911 Burton 3911 tie (same name and ID) copy one, i++ and j++ Burton 3911
4 Burton 4944 Dillard 6552 list1 name is less copy list1[i], i++ Burton 4944
5 Franz 1692 Dillard 6552 list1 name is greater copy list2[j], j++ Dillard 6552

Final result: [Aaron 1729, Arthur 4920, Baker 2921, Burton 3911, Burton 4944, Dillard 6552] — this matches the problem's expected result array exactly, including the tie at k = 3, where the Burton 3911 that appears in both input arrays is copied into result only once even though the loop advanced past a matching entry in both list1 and list2 on that step.

Common Mistakes to Avoid

  • Only advancing one index on a tie (e.g., always incrementing just i when comparison == 0). This leaves the matching entry in the other list still "next in line," so it gets compared — and potentially copied — again on a later iteration, producing a duplicate.
  • Looping based on list1.length or list2.length instead of result.length. The whole point of a prefix merge is stopping once result is full, which can happen well before either input array is exhausted.
  • Creating a temporary array, ArrayList, or other collection as scratch space. The problem explicitly calls this out as not receiving full credit — the merge has to write directly into the given result array.
  • Forgetting that list1 and list2 must not be modified. Since the code above only reads from them (never assigns into list1[...] or list2[...]), this postcondition is satisfied automatically as long as nothing is added to reassign their elements.

Key Takeaways

  • A comparison method that orders by more than one field checks the primary field first and only looks at the secondary field when the primary one ties — returning early keeps the tie-breaking logic from ever running unnecessarily.
  • Merging two sorted collections into a third is a three-pointer pattern: one index for each input, one for the output, advancing whichever input pointer "loses" the comparison (or both, on a tie).
  • A "prefix" merge — filling a fixed-size output that's shorter than either input — is bounded by the output's length, not either input's, since the algorithm is guaranteed to have enough source elements to finish.

Related FRQs