ClubMembers: 2021 FRQ 3
A step-by-step solution to the 2021 AP CSA FRQ 3 (ClubMembers), covering building an ArrayList from an array and safely removing elements while iterating in Java.
Keeping track of a club roster is the scenario behind this AP Computer Science A free-response question — you add a batch of new members from a plain array, then remove graduated members from an ArrayList while simultaneously collecting the ones worth recognizing.
What This FRQ Tests
- AP CSA units: Unit 6 (Array) and Unit 7 (ArrayList)
- Core skill: looping through an array to build up an existing
ArrayList - Secondary skill: safely removing elements from an
ArrayListwhile looping over it, without skipping elements or corrupting the list - Official category: "Array/ArrayList" — always FRQ 3 on the AP CSA exam
The Setup
- The given
MemberInfoclass has:- A constructor:
MemberInfo(String name, int gradYear, boolean hasGoodStanding) int getGradYear()andboolean inGoodStanding()getters
- A constructor:
- The
ClubMembersclass holds:private ArrayList<MemberInfo> memberList
- You're asked to write two methods:
addMembers(String[] names, int gradYear)— adds a batch of new members, all sharing one graduation yearremoveMembers(int year)— removes every graduated member from the list, returning just the graduated ones who are in good standing
Part (a): Writing addMembers(String[] names, int gradYear)
The Rule, Broken Down
- Every name in the
namesarray becomes a new club member. - Every new member shares the same graduation year,
gradYear. - Every new member starts out in good standing.
- Order doesn't matter — the names can be added in any order.
Step-by-Step Approach
- Loop over every index of the
namesarray. - At each index, construct a new
MemberInfousing that name, the sharedgradYear, andtruefor good standing. - Add that new
MemberInfoobject tomemberList.
The Code
public void addMembers(String[] names, int gradYear)
{
for (int i = 0; i < names.length; i++)
{
memberList.add(new MemberInfo(names[i], gradYear, true));
}
}
Why Each Piece Matters
names.length, notmemberList.size()— the loop walks the array of incoming names, not the list being built up.new MemberInfo(names[i], gradYear, true)— a brand-new object is constructed for every name; you can't add aStringdirectly to anArrayList<MemberInfo>, it has to be wrapped.truefor the third constructor argument — the rule explicitly says every newly added member starts in good standing, so this is alwaystrue, never based on any condition.memberList.add(...)— the no-index version ofaddappends to the end of the list, which is fine since order doesn't matter here.
Common Mistakes to Avoid
- Looping over
memberListinstead ofnames. The array being read from and the list being written to are two different collections with two different lengths. - Forgetting to wrap each name in a
new MemberInfo(...).memberListholdsMemberInfoobjects, not rawStrings. - Hardcoding
falseor leaving good standing unset. Every member added by this method starts in good standing, by rule.
Part (b): Writing removeMembers(int year)
The Rule, Broken Down
- A member has "graduated" if their graduation year is less than or equal to the
yearparameter. - Every graduated member gets removed from
memberList— regardless of whether they're in good standing. - The method returns a separate list containing only the graduated members who were also in good standing.
- Members who haven't graduated yet are left alone in
memberList, untouched.
Step-by-Step Approach
- Create an empty
ArrayList<MemberInfo>to collect the graduated-and-in-good-standing members. - Walk through
memberListusing a manually tracked index, rather than a simpleforloop, because elements are going to be removed mid-loop. - At each position, check whether that member has graduated (
getGradYear() <= year). - If they haven't graduated, move on to the next index normally.
- If they have graduated: first check if they're in good standing and, if so, add them to the result list — then remove them from
memberListat the current index, and don't advance the index (the next member has just shifted into this position). - After the loop finishes, return the result list.
The Code
public ArrayList<MemberInfo> removeMembers(int year)
{
ArrayList<MemberInfo> graduatedGoodStanding = new ArrayList<MemberInfo>();
int i = 0;
while (i < memberList.size())
{
MemberInfo member = memberList.get(i);
if (member.getGradYear() <= year)
{
if (member.inGoodStanding())
{
graduatedGoodStanding.add(member);
}
memberList.remove(i);
}
else
{
i++;
}
}
return graduatedGoodStanding;
}
Why Each Piece Matters
- A
whileloop with a manually managed index, instead of afor (int i = 0; i < memberList.size(); i++)loop — removing an element shrinks the list and shifts every later element one position to the left. Aforloop's automatici++would then skip over the element that just slid into the removed spot. i++only happens in theelsebranch — when a member is removed, the very next member is now sitting at that same indexi, so the loop needs to check indexiagain on the next pass, not move past it.- Checking
inGoodStanding()before removing — both actions depend on the samememberreference, so order between them doesn't actually matter here, but the good-standing check has to happen while you still havememberin hand (which you always do, since it was already read into a variable). memberList.size()re-evaluated every loop iteration — because it's in thewhilecondition (not stored in a variable beforehand), it automatically reflects the list's new, smaller size after each removal.
Tracing the Example
Starting memberList (before removeMembers(2018)):
| Index | Name | Grad Year | Good Standing |
|---|---|---|---|
| 0 | "SMITH, JANE" |
2019 | false |
| 1 | "FOX, STEVE" |
2018 | true |
| 2 | "XIN, MICHAEL" |
2017 | false |
| 3 | "GARCIA, MARIA" |
2020 | true |
Walking through with i starting at 0:
i = 0: Jane, grad year 2019 —2019 <= 2018isfalse, not graduated →i++→i = 1i = 1: Fox, grad year 2018 —2018 <= 2018istrue, graduated. Good standing istrue→ add Fox to the result list. Remove index 1. List is now[Jane, Xin, Garcia].istays1.i = 1again: the list shifted, so index 1 is now Xin, grad year 2017 — graduated. Good standing isfalse→ don't add. Remove index 1. List is now[Jane, Garcia].istays1.i = 1again: index 1 is now Garcia, grad year 2020 —2020 <= 2018isfalse, not graduated →i++→i = 2i = 2:memberList.size()is now2, so the loop ends.
Final memberList: [Jane, Garcia] — matches the question's expected "after" list exactly. Returned list: [Fox] — matches the question's expected returned list exactly.
Common Mistakes to Avoid
- Using a
forloop withi++in the update clause while also removing elements inside the loop. This is the single most common bug on this problem — it silently skips the element that shifts into the just-removed position. - Incrementing
iunconditionally, even after a removal. If a removal happened, incrementingiskips the next element entirely. - Filtering by
inGoodStanding()alone and forgetting the graduation-year check, or vice versa — the returned list needs graduated and in good standing; removal frommemberListneeds graduated regardless of standing. Those are two different conditions applied to two different actions. - Removing non-graduated members, or leaving graduated-but-not-good-standing members in
memberList. Every graduated member is removed, whether or not they end up in the returned list.
Key Takeaways
- Removing elements from an
ArrayListwhile iterating over it requires a manually tracked index (or iterating backward) — never rely on aforloop's automatic increment when the list itself might shrink mid-loop. - When an index update depends on whether something happened this iteration, an
if/elsearound the increment (rather than an unconditionali++) is the cleanest way to express that. - "Filter into a new list while also modifying the original" problems almost always have two separate conditions in play — read carefully to see whether they're the same condition or different ones.
Need help preparing for the AP exam?
FRQs are one of the toughest parts of the AP CS exam. I offer 1-on-1 tutoring to help you work through practice problems, tighten up your responses, and build the confidence to earn full credit on exam day.
Book a tutoring session →