Review / ReviewAnalysis: 2022 FRQ 3
A step-by-step solution to the 2022 AP CSA FRQ 3 (ReviewAnalysis), covering array traversal, accumulation, and building an ArrayList from filtered data in Java.
Summarizing an array of custom objects two different ways is the challenge in this AP Computer Science A free-response question — first as a plain numeric average, then as a filtered, reformatted ArrayList of text.
What This FRQ Tests
- AP CSA units: Unit 6 (Array) and Unit 7 (ArrayList)
- Core skill: looping through an array of objects to accumulate a numeric result
- Secondary skill: building a new
ArrayListby selectively picking and transforming elements from an existing array - Official category: "Array/ArrayList" — always FRQ 3 on the AP CSA exam
The Setup
- The given
Reviewclass has:private int ratingprivate String comment— either an empty string, or text ending in a letter, a period (.), or an exclamation point (!)int getRating()andString getComment()getters
- The
ReviewAnalysisclass holds:private Review[] allReviews— a plain array, not anArrayList
- You're asked to write two methods:
getAverageRating()— the arithmetic mean of every rating in the arraycollectComments()— a filtered, reformattedArrayList<String>built from the array
Part (a): Writing getAverageRating()
Step-by-Step Approach
- Set up a running total, starting at
0. - Loop over every index of
allReviews. - On each iteration, get that
Review's rating and add it to the total. - After the loop, divide the total by the number of reviews — as a
double, not anint.
The Code
public double getAverageRating()
{
int total = 0;
for (int i = 0; i < allReviews.length; i++)
{
total += allReviews[i].getRating();
}
return (double) total / allReviews.length;
}
Why the (double) Cast Is Required
totalandallReviews.lengthare both declared asint.- Java's
/operator performs integer division when both operands areints — it truncates any decimal, so7 / 2evaluates to3, not3.5. - Casting
totaltodoublebefore the division forces Java to treat the whole expression as floating-point math, so the decimal part is preserved. - Casting the result instead —
(double) (total / allReviews.length)— would not work, because the truncation already happened before the cast was applied.
Common Mistakes to Avoid
- Casting in the wrong place —
(double)(total / allReviews.length)truncates first, then converts; the.0you get back is meaningless. - Casting
allReviews.lengthinstead oftotal(or not casting at all) — only one side of a division needs to be adoublefor Java to perform floating-point division, but skipping the cast entirely gives you truncated integer math. - Declaring
totalas adoublefrom the start. This technically also works, but mixing anintaccumulator with a single explicit cast at the return statement is the more common, rubric-friendly style.
Part (b): Writing collectComments()
The Rule, Broken Down
- Look at every comment in
allReviews. - Keep only the ones that contain an exclamation point (
!) anywhere in the text. - Format each kept comment as
"index-comment"(the review's array index, a hyphen, then the original comment). - If the comment doesn't already end in a period or exclamation point, add a period to the end.
- Collect all the formatted strings into an
ArrayList<String>and return it.
Step-by-Step Approach
- Create an empty
ArrayList<String>to hold the results. - Loop over every index of
allReviews. - Pull out that review's comment.
- Check whether the comment contains
"!"— skip it entirely if not. - If it does, build the
"index-comment"string. - Check the comment's last character. If it isn't
.or!, append a period. - Add the finished string to the
ArrayList. - After the loop, return the
ArrayList.
The Code
public ArrayList<String> collectComments()
{
ArrayList<String> comments = new ArrayList<String>();
for (int i = 0; i < allReviews.length; i++)
{
String comment = allReviews[i].getComment();
if (comment.indexOf("!") != -1)
{
String formatted = i + "-" + comment;
String lastChar = comment.substring(comment.length() - 1);
if (!lastChar.equals(".") && !lastChar.equals("!"))
{
formatted = formatted + ".";
}
comments.add(formatted);
}
}
return comments;
}
Why Each Piece Matters
comment.indexOf("!") != -1—indexOfreturns the position of the first match, or-1if the character never appears anywhere in the string. Comparing against-1is the standard way to ask "does this string contain X?" using a method that's actually on the AP Quick Reference sheet (containsisn't listed there, though using it wouldn't be wrong — see the Notes section below).comment.substring(comment.length() - 1)—charAtwould return the last character more directly, but it isn't on the Quick Reference sheet either, so this solution sticks tosubstringto stay within what's guaranteed to be there for you to look up. It's a common substitute pattern worth memorizing.!lastChar.equals(".") && !lastChar.equals("!")— both conditions have to be true (the last character is neither a period nor an exclamation point) before a period gets appended.- The
iini + "-" + comment— Java automatically converts theintindex to aStringwhen it's concatenated with+, so no explicit conversion is needed.
Tracing the Example
Using the sample data from the question — five reviews with comments "Good! Thx", "OK site", "Great!", "Poor! Bad.", "":
| Index | Comment | Contains !? |
Last char | Result |
|---|---|---|---|---|
| 0 | "Good! Thx" |
yes | x (not . or !) |
"0-Good! Thx." |
| 1 | "OK site" |
no | — | (skipped) |
| 2 | "Great!" |
yes | ! |
"2-Great!" |
| 3 | "Poor! Bad." |
yes | . |
"3-Poor! Bad." |
| 4 | "" |
no | — | (skipped) |
Final result: ["0-Good! Thx.", "2-Great!", "3-Poor! Bad."] — matches the question's expected output exactly.
Common Mistakes to Avoid
- Checking for
!withequals("!")instead ofindexOf.equalschecks the entire string, not whether it contains a character — this would only match a comment that is exactly"!". - Appending a period unconditionally. Comments already ending in
.or!should be left alone — check first. - Forgetting the empty-string case. An empty comment (
"") has no!, so it's correctly skipped by theindexOfcheck — but callingsubstring(comment.length() - 1)on an empty string would throw an exception, which is why that line only ever runs after the!check has already confirmed the comment isn't empty. - Off-by-one on the index. Use the loop variable
idirectly — don't add or subtract 1.
Notes: Methods Not on the AP CSA Quick Reference Sheet
A first-year (non-AP) Java course often teaches a few String methods that read a bit more naturally. They're not on the AP Quick Reference sheet, but that's not the same as being disallowed — any correct Java is accepted on the real exam:
public ArrayList<String> collectComments()
{
ArrayList<String> comments = new ArrayList<String>();
for (int i = 0; i < allReviews.length; i++)
{
String comment = allReviews[i].getComment();
if (comment.contains("!"))
{
String formatted = i + "-" + comment;
char lastChar = comment.charAt(comment.length() - 1);
if (lastChar != '.' && lastChar != '!')
{
formatted = formatted + ".";
}
comments.add(formatted);
}
}
return comments;
}
contains("!")replaces theindexOf("!") != -1check.charAt(...)grabs the last character directly as achar, instead of pulling out a one-characterStringwithsubstring.- Both are completely valid Java, and using them on the real exam is fine if you're confident in them — the only real tradeoff is that you can't look their exact behavior up on the reference sheet if you second-guess yourself mid-exam, the way you could with
indexOforsubstring.
Key Takeaways
- When a method on the AP Quick Reference sheet isn't quite what you're used to (no
contains, nocharAt), there's almost always anindexOf/substring-based equivalent. - Integer division truncates — cast to
doublebefore dividing, not after. - Filtering-and-transforming into a new
ArrayListis a three-step loop pattern: check a condition, build the new value, add it.