CompSci.rocks
FRQcsapa

WordList: 2004 FRQ 1

A step-by-step solution to the 2004 AP CSA FRQ 1 (WordList), covering looping through an ArrayList to count and remove elements by length in Java.

A list of words stored in an ArrayList gets counted and filtered by length in this AP Computer Science A free-response question — first tallying how many words match a given length, then removing all of them while keeping everything else exactly where it was.

What This FRQ Tests

  • AP CSA units: Unit 6 (Array) and Unit 7 (ArrayList)
  • Core skill: looping through an ArrayList by index to count matching elements
  • Secondary skill: removing selected elements from an ArrayList mid-loop without skipping any or disturbing the order of what's left
  • Official category: "Array/ArrayList," which on the 2004 exam was FRQ 1 — the fixed FRQ 1–4 category order used in more recent years (Methods and Control Structures, Classes, Array/ArrayList, 2D Array, always in that sequence) wasn't standardized until the 2019–2020 Course and Exam Description redesign. 2004's actual printed order was WordList (Array/ArrayList), the Pet/Cat/LoudDog/Kennel hierarchy (Classes), a Marine Biology Simulation question (skipped on this site — see the note below), and Robot (Methods and Control Structures).

The Setup

  • The WordList class has one field: private ArrayList myList; — a list of Strings made up of letters.
  • Notice myList is declared as a plain ArrayList, with no <String> type in sight. That's typical of AP CSA code from this era, before generics became the standard way to declare a collection's element type. It means myList.get(i) hands back a plain Object, which has to be cast to String before any String method can be called on it.
  • You're asked to write two methods:
    • numWordsOfLength(int len) — counts how many words are exactly len letters long
    • removeWordsOfLength(int len) — removes every word that's exactly len letters long, keeping the rest in their original order

Part (a): Writing numWordsOfLength(int len)

The Rule, Broken Down

numWordsOfLength returns the number of words in myList whose length is exactly len — not "at least," not "at most," exactly.

Step-by-Step Approach

  1. Start a counter at 0.
  2. Loop over every index of myList.
  3. Pull out that element and cast it to String.
  4. If its length equals len, increment the counter.
  5. After the loop, return the counter.

The Code

public int numWordsOfLength(int len)
{
    int count = 0;

    for (int i = 0; i < myList.size(); i++)
    {
        String word = (String) myList.get(i);

        if (word.length() == len)
        {
            count++;
        }
    }

    return count;
}

Why Each Piece Matters

  • myList.size(), not a hardcoded number — the loop bound has to match however many words happen to be stored, which can change from one WordList to another.
  • (String) myList.get(i) — since myList isn't declared with a generic type, get(i) returns type Object. Calling .length() directly on that Object wouldn't compile; the cast tells the compiler "trust me, this is really a String."
  • ==, not >= or <= — the rule is "exactly len letters," so anything other than an exact match should be skipped.

Tracing the Example

Using the question's own data — animals.myList contains ["cat", "mouse", "frog", "dog", "dog"]:

Word Length Matches len = 4? Matches len = 3? Matches len = 2?
"cat" 3 no yes no
"mouse" 5 no no no
"frog" 4 yes no no
"dog" 3 no yes no
"dog" 3 no yes no
  • animals.numWordsOfLength(4)1 (only "frog")
  • animals.numWordsOfLength(3)3 ("cat" and both "dog"s)
  • animals.numWordsOfLength(2)0 (nothing is 2 letters long)

All three match the question's table exactly.

Common Mistakes to Avoid

  • Forgetting the (String) cast. Without it, word.length() won't compile at all, since Object has no length() method.
  • Using >= or <= instead of ==. This method counts exact-length matches only.
  • Looping with i <= myList.size(). That reads one index past the end of the list on the final iteration, throwing an IndexOutOfBoundsException.

Part (b): Writing removeWordsOfLength(int len)

The Rule, Broken Down

  1. Remove every word from myList that's exactly len letters long.
  2. Leave the relative order of whatever remains unchanged.

Step-by-Step Approach

  1. Loop backward, starting at the last index (myList.size() - 1) and going down to 0.
  2. Cast each element to String and check its length.
  3. If it matches len, remove it at that index.
  4. Once the loop finishes, every matching word is gone.

Looping backward is the key idea here: when an element is removed from an ArrayList, every element after it shifts down by one index to fill the gap. Removing while walking forward risks skipping the element that just slid into the spot you already passed. Walking backward avoids the problem entirely, since every index still to be checked is before the removal point and is never touched by the shift.

The Code

public void removeWordsOfLength(int len)
{
    for (int i = myList.size() - 1; i >= 0; i--)
    {
        String word = (String) myList.get(i);

        if (word.length() == len)
        {
            myList.remove(i);
        }
    }
}

Why Each Piece Matters

  • i = myList.size() - 1, counting down — this is what makes it safe to remove elements mid-loop; every remaining index still points at an element that hasn't been checked yet, regardless of how many removals already happened at higher indices.
  • myList.remove(i) — removes by position, not by value, which is exactly what's needed once the matching index has already been found.
  • No return value — this method's signature is void; it mutates myList directly instead of building and returning a new list.

Tracing the Example

Using the question's own sequence of calls, starting from ["cat", "mouse", "frog", "dog", "dog"]:

animals.removeWordsOfLength(4):

i Word Length Match? Action
4 "dog" 3 no keep
3 "dog" 3 no keep
2 "frog" 4 yes remove
1 "mouse" 5 no keep
0 "cat" 3 no keep

Result: ["cat", "mouse", "dog", "dog"] — matches the question's expected list.

animals.removeWordsOfLength(3), continuing from that result:

i Word Length Match? Action
3 "dog" 3 yes remove
2 "dog" 3 yes remove
1 "mouse" 5 no keep
0 "cat" 3 yes remove

Result: ["mouse"] — matches.

animals.removeWordsOfLength(2), continuing from ["mouse"]: "mouse" is 5 letters, no match, so the list stays ["mouse"] — matches.

Common Mistakes to Avoid

  • Looping forward with a plain i++ while removing. After a removal, the element that used to be at i + 1 slides into position i, so the very next iteration (i + 1) skips right over it.
  • Using a for-each loop (for (Object o : myList)) and removing inside it. Modifying an ArrayList's contents while iterating it with a for-each loop throws a ConcurrentModificationException.
  • Forgetting the (String) cast — same issue as part (a); myList still isn't a generic ArrayList<String>.

Key Takeaways

  • Counting matches in an ArrayList is a simple forward loop, but removing matches safely means looping backward, since removal shifts every later index down by one.
  • An ArrayList declared without a generic type (ArrayList instead of ArrayList<String>) returns plain Objects from get(...), and needs an explicit cast before calling any type-specific method.
  • "Exactly matches" conditions use == for numeric comparisons like length — reserve .equals() for comparing the actual text of two Strings.

Related FRQs