FRQ
› csapa
ArrayResizer: 2021 FRQ 4
A step-by-step solution to the 2021 AP CSA FRQ 4 (ArrayResizer), covering 2D array traversal and building a smaller 2D array from selected rows in Java.
Trimming down a two-dimensional array to just its "clean" rows is the challenge in this AP Computer Science A free-response question — first testing a single row for zeros, then using that test (plus a provided helper) to build a smaller replacement array.
What This FRQ Tests
- AP CSA units: Unit 8 (2D Array) and Unit 4 (Iteration)
- Core skill: looping across one row of a 2D array to check every value in it
- Secondary skill: allocating a brand-new 2D array at the correct size up front, since Java arrays can't be resized after creation
- Official category: "2D Array" — always FRQ 4 on the AP CSA exam
The Setup
ArrayResizerhas threestaticmethods (this problem doesn't use instance fields at all — everything operates on anint[][]passed in as a parameter):boolean isNonZeroRow(int[][] array2D, int r)— for you to write in part (a)int numNonZeroRows(int[][] array2D)— already written for you; counts how many rows contain no zeros at allint[][] resize(int[][] array2D)— for you to write in part (b), using the other two methods
- Despite the "Array/ArrayList"-sounding name, this problem is squarely a 2D array question — every method takes an
int[][]parameter, not a 1D array or anArrayList.
Part (a): Writing isNonZeroRow(int[][] array2D, int r)
The Rule, Broken Down
- Look at every value in row
ronly — not the whole 2D array. - If every value in that row is non-zero, return
true. - If any value in that row is zero, return
false.
Step-by-Step Approach
- Loop across every column index of row
r. - At each column, check whether that value is
0. - The moment a zero is found, the row already fails the test — return
falseimmediately, no need to keep checking. - If the loop finishes without ever finding a zero, the row passed — return
true.
The Code
public static boolean isNonZeroRow(int[][] array2D, int r)
{
for (int c = 0; c < array2D[r].length; c++)
{
if (array2D[r][c] == 0)
{
return false;
}
}
return true;
}
Why Each Piece Matters
array2D[r].length, notarray2D.length—array2D[r]is itself a 1D array (rowr), and its.lengthgives the number of columns in that specific row.array2D.lengthwould instead give the total number of rows in the whole 2D array, which is the wrong number to loop against here.- Returning
falseimmediately inside the loop — as soon as one zero is found, there's no need to look at the rest of the row; the answer is already decided. This also naturally avoids ever needing a separate boolean flag variable. - Returning
trueonly after the loop finishes — this is what correctly handles the case where the loop finds no zeros at all, including the edge case of checking every single column without a single early return firing.
Tracing the Example
Using the array from the question:
int[][] arr = { {2, 1, 0},
{1, 3, 2},
{0, 0, 0},
{4, 5, 6} };
| Call | Row checked | Values | Result |
|---|---|---|---|
isNonZeroRow(arr, 0) |
row 0 | 2, 1, 0 | false — hits a 0 at column 2 |
isNonZeroRow(arr, 1) |
row 1 | 1, 3, 2 | true — no zero anywhere |
isNonZeroRow(arr, 2) |
row 2 | 0, 0, 0 | false — hits a 0 at column 0 |
isNonZeroRow(arr, 3) |
row 3 | 4, 5, 6 | true — no zero anywhere |
All four results match the table given in the question.
Common Mistakes to Avoid
- Looping with
array2D.lengthinstead ofarray2D[r].length. This is the classic 2D array mix-up — one gives the row count, the other gives a specific row's column count, and using the wrong one either misses columns or throws anArrayIndexOutOfBoundsException. - Continuing to loop after finding a zero, using a boolean flag instead of returning early. This works too, but it's easy to get the flag logic backwards; returning
falsethe instant a zero shows up is simpler and harder to mess up. - Checking
array2D[r][c] != 0and trying to build the logic around that instead of the zero case. Either direction can work, but starting from "is this the failing condition?" (a zero) keeps the early-return pattern clean.
Part (b): Writing resize(int[][] array2D)
The Rule, Broken Down
- Build a new 2D array containing only the rows from
array2Dthat have no zeros. - Keep those rows in the same relative order they appeared in originally.
- The original
array2Dmust be left unchanged. - You're required to actually use both
numNonZeroRowsandisNonZeroRowto receive full credit — not reimplement their logic yourself.
Step-by-Step Approach
- Java arrays can't grow or shrink after creation, so figure out the new array's size before creating it. That's exactly what
numNonZeroRows(array2D)is for — call it once to get the row count for the new array. - Create the new 2D array with that many rows, and the same number of columns as the original (
array2D[0].length). - Keep a separate counter for which row of the new array to fill in next, starting at
0. - Loop over every row index of the original
array2D. - For each row, call
isNonZeroRowto check if it qualifies. If it does, copy every value in that row into the next open row of the new array, then advance the new-array row counter. - After the loop, return the new array.
The Code
public static int[][] resize(int[][] array2D)
{
int[][] result = new int[numNonZeroRows(array2D)][array2D[0].length];
int newRow = 0;
for (int r = 0; r < array2D.length; r++)
{
if (isNonZeroRow(array2D, r))
{
for (int c = 0; c < array2D[r].length; c++)
{
result[newRow][c] = array2D[r][c];
}
newRow++;
}
}
return result;
}
Why Each Piece Matters
numNonZeroRows(array2D)used for the new array's row count — this is precisely why that helper method was provided. Without knowing the count in advance, there'd be no way to size a plain Java array correctly (unlike anArrayList, which can just grow as you go).isNonZeroRow(array2D, r)used to decide whether to copy rowr— reusing part (a)'s method here means the logic for "what counts as a good row" only lives in one place, matching the rubric's requirement to actually use it rather than duplicate its logic inline.- A separate
newRowcounter, distinct fromr—rwalks every row of the original array (including skipped ones), whilenewRowonly advances when a row is actually copied. These two numbers fall out of sync as soon as the first zero-containing row is skipped, which is exactly why they need to be tracked separately. - The inner loop copies one row at a time — since rows are themselves arrays, an entire row can't be assigned in one step (
result[newRow] = array2D[r];would copy a reference to the original row's array rather than its values, which technically works for reading here but is fragile style; looping value-by-value is the safer, more explicit habit).
Tracing the Example
Using the same array as part (a):
int[][] arr = { {2, 1, 0},
{1, 3, 2},
{0, 0, 0},
{4, 5, 6} };
numNonZeroRows(arr)→2(only rows 1 and 3 qualify), soresult = new int[2][3].r = 0:isNonZeroRow(arr, 0)isfalse— skipped,newRowstays0.r = 1:isNonZeroRow(arr, 1)istrue— copy{1, 3, 2}intoresult[0], thennewRowbecomes1.r = 2:isNonZeroRow(arr, 2)isfalse— skipped,newRowstays1.r = 3:isNonZeroRow(arr, 3)istrue— copy{4, 5, 6}intoresult[1], thennewRowbecomes2.
Final result: { {1, 3, 2}, {4, 5, 6} } — matches the question's expected contents of smaller exactly.
Common Mistakes to Avoid
- Not using
numNonZeroRowsat all (e.g., guessing a size, or building anArrayListand converting it) — the problem explicitly requires using it for full credit, and it's also the only clean way to know the new array's size before creating it. - Sizing the new array with
array2D.lengthinstead ofnumNonZeroRows(array2D). That would create a new array the same size as the original, defeating the entire point of "resizing" it, and leaving trailing rows filled with zeros. - Advancing
newRowon every iteration of the outer loop, instead of only when a row is actually copied. This leaves gaps (all-zero rows) in the new array instead of a tightly packed result. - Using
array2D.lengthfor the number of columns instead ofarray2D[0].length. Rows and columns are easy to swap by accident — always double check which dimension a given.lengthis measuring.
Key Takeaways
array2D.lengthgives the number of rows;array2D[someRow].lengthgives the number of columns in that row — mixing these up is the most common 2D array bug on the AP exam.- Plain Java arrays are fixed-size the moment they're created, so "build a new array containing only some elements" always starts with figuring out the final size first — often via a helper method written just for that purpose.
- When copying selected rows into a smaller array, track two separate indices: one walking the original structure, one walking the new one — they only stay in sync until the first item gets skipped.