WeatherData: 2023 FRQ 3
A step-by-step solution to the 2023 AP CSA FRQ 3 (WeatherData), covering safely removing elements from an ArrayList while iterating and tracking the longest run of consecutive values in Java.
Cleaning up a season's worth of daily high temperatures and then hunting for the longest hot streak inside them is what drives this AP Computer Science A free-response question — one method trims bad data out of an ArrayList, the other tracks a running streak across it.
What This FRQ Tests
- AP CSA units: Unit 6 (Array) and Unit 7 (ArrayList)
- Core skill: removing elements from an
ArrayListwhile looping over it, without skipping elements or crashing - Secondary skill: tracking a running streak length across consecutive elements, and remembering the best streak seen so far
- Official category: "Array/ArrayList" — always FRQ 3 on the AP CSA exam
The Setup
WeatherDataholds one field:private ArrayList<Double> temperatures— guaranteed non-null, with nonullentries.- You're asked to write two methods:
void cleanData(double lower, double upper)— removes every value belowloweror aboveupper, keeping the remaining values in their original orderint longestHeatWave(double threshold)— returns the length of the longest run of consecutive days all abovethreshold(a "heat wave" needs at least 2 such days in a row; the data is guaranteed to contain at least one)
Part (a): Writing cleanData(double lower, double upper)
The Rule, Broken Down
- Any value strictly less than
lowergets removed. - Any value strictly greater than
uppergets removed. - Everything else stays, in its original relative order.
Step-by-Step Approach
- Loop over the
ArrayList's indices — but from the last index down to0, not the usual0up tosize() - 1. - At each index, get that value and check whether it's out of range.
- If it's out of range, remove it at that index right away.
- Let the loop keep going until every index (checked in this backwards order) has been visited.
The Code
public void cleanData(double lower, double upper)
{
for (int i = temperatures.size() - 1; i >= 0; i--)
{
double temp = temperatures.get(i);
if (temp < lower || temp > upper)
{
temperatures.remove(i);
}
}
}
Why Looping Backwards Is Required
ArrayList.remove(index)shifts every later element one position to the left, and shrinkssize()by 1.- If you loop forward and remove at index
i, the element that used to be ati + 1slides into indexi— but the loop's next iteration checks indexi + 1, skipping right over it. - Looping from the last index down to
0sidesteps this entirely: removing indexionly ever shifts elements at indices greater thani, all of which the backwards loop has already finished checking. Every remaining, not-yet-visited index still lines up with the value it's supposed to hold.
Tracing the Example
Using the question's sample data — indices 0–9 hold 99.1, 142.0, 85.0, 85.1, 84.6, 94.3, 124.9, 98.0, 101.0, 102.5 — with cleanData(85.0, 120.0):
| Index checked (backwards) | Value | In range [85.0, 120.0]? | Action |
|---|---|---|---|
| 9 | 102.5 | yes | keep |
| 8 | 101.0 | yes | keep |
| 7 | 98.0 | yes | keep |
| 6 | 124.9 | no (> 120.0) | remove |
| 5 | 94.3 | yes | keep |
| 4 | 84.6 | no (< 85.0) | remove |
| 3 | 85.1 | yes | keep |
| 2 | 85.0 | yes (not less than 85.0) | keep |
| 1 | 142.0 | no (> 120.0) | remove |
| 0 | 99.1 | yes | keep |
Final contents: 99.1, 85.0, 85.1, 94.3, 98.0, 101.0, 102.5 — matches the question's expected result exactly, with the order of the surviving values preserved.
Common Mistakes to Avoid
- Looping forward with
i++while removing elements. This is the single most common bug on this style of problem — it silently skips checking every element that shifts into a just-vacated spot. - Using
<=instead of<for the lower bound, or>=instead of>for the upper bound. The rule removes values less thanlowerand greater thanupper— a value exactly equal to either bound must be kept, as85.0is in the traced example. - Calling
temperatures.size()fresh inside the loop condition instead of capturing it once (or, as here, counting down from a value computed before removals start). It isn't wrong to callsize()again in a backwards loop, but many students writei < temperatures.size()out of habit — which only makes sense for a forward loop, and would misbehave here.
Part (b): Writing longestHeatWave(double threshold)
The Rule, Broken Down
- A heat wave is 2 or more consecutive days with a temperature strictly greater than
threshold. - The answer is the length of the longest such run anywhere in the data.
- The data is guaranteed to contain at least one heat wave, so the true answer is always at least 2 — nothing special has to be done to rule out a lone single-day spike being reported as the answer.
Step-by-Step Approach
- Keep two counters:
current, the length of the streak in progress, andlongest, the best streak seen so far. Both start at0. - Loop through every temperature in order.
- If the current temperature is above
threshold, extend the streak:current++. - If it's not, the streak is broken: reset
currentback to0. - After updating
currenteach iteration, check whether it's now the new best, and updatelongestif so. - After the loop,
longestholds the answer.
The Code
public int longestHeatWave(double threshold)
{
int longest = 0;
int current = 0;
for (int i = 0; i < temperatures.size(); i++)
{
if (temperatures.get(i) > threshold)
{
current++;
}
else
{
current = 0;
}
if (current > longest)
{
longest = current;
}
}
return longest;
}
Why Each Piece Matters
currentresets to0, not1, when a day fails the threshold. A broken streak means the next qualifying day starts a brand-new streak of length 1 on its own next iteration, not a continuation of anything.longestis updated every iteration, not just when a streak ends. Checkingcurrent > longestright after updatingcurrentcorrectly captures a streak's length even if it's still growing when the data runs out.- The precondition (there's guaranteed to be at least one real heat wave) is what makes this simple loop sufficient — since a heat wave requires length 2+, and the longest run overall can never be shorter than any heat wave that exists, the maximum run length this loop finds is guaranteed to be an actual heat wave's length, not a stray single day.
Tracing the Example
Using the question's sample data — 100.5, 98.5, 102.0, 103.9, 87.5, 105.2, 90.3, 94.8, 109.1, 102.1, 107.4, 93.2 — with longestHeatWave(100.5):
| Value | > 100.5? | current after |
longest after |
|---|---|---|---|
| 100.5 | no | 0 | 0 |
| 98.5 | no | 0 | 0 |
| 102.0 | yes | 1 | 1 |
| 103.9 | yes | 2 | 2 |
| 87.5 | no | 0 | 2 |
| 105.2 | yes | 1 | 2 |
| 90.3 | no | 0 | 2 |
| 94.8 | no | 0 | 2 |
| 109.1 | yes | 1 | 2 |
| 102.1 | yes | 2 | 2 |
| 107.4 | yes | 3 | 3 |
| 93.2 | no | 0 | 3 |
Final result: 3 — matching the question exactly (the two-day run at 102.0, 103.9 and the three-day run at 109.1, 102.1, 107.4 are the two heat waves, and 3 is the longer one). Re-running the same trace with longestHeatWave(95.2) extends the very first run to four days (100.5, 98.5, 102.0, 103.9 are all above 95.2), correctly producing 4, also matching the question.
Common Mistakes to Avoid
- Resetting
currentto1instead of0on a failed day. This overcounts every streak by one and miscounts consecutive short streaks as connected. - Only checking
current > longestafter the streak ends (e.g., inside theelsebranch) instead of every iteration. This misses a streak that's still in progress when the loop reaches the last element. - Using
>=instead of>when comparing againstthreshold. The rule is strictly "greater than," so a temperature exactly equal to the threshold does not extend a heat wave. - Forgetting a heat wave requires at least 2 days and trying to special-case single-day spikes explicitly — the precondition already guarantees this isn't necessary, since the true longest run can't be shorter than 2.
Key Takeaways
- Removing elements from an
ArrayListwhile iterating over it means looping backwards, from the last index to0— forward loops skip elements after a removal. - "Longest run of consecutive elements meeting a condition" is a two-variable pattern: a
currentcounter that grows or resets each iteration, and alongestcounter that's checked (and possibly updated) every single timecurrentchanges. - A precondition that guarantees a result exists (like "there's at least one heat wave") can simplify a solution — it means you don't have to defend against an edge case that's already ruled out.