Seating Chart: Ragged Arrays, Enhanced For and Off-By-One Loops
An exam centre is writing a seating tool. A utility line declares int[] rowA, rowB[];. The main structure is int[][] seats = new int[4][];. Rows 0 and 2 are then assigned with seats[0] = new int[]{12, 14, 16}; and seats[2] = new int[]{11, 13};; rows 1 and 3 are never assigned. Two verification loops are added. The first is a for-each: for (int[] row : seats) { total += row.length; }, which throws NullPointerException. The second is for (int i = 0; i <= seats.length; i++) { total += seats[i].length; }, which fails differently. A junior developer claims rowB is an int[] just like rowA, and a second developer claims that zeroing an array with for (int v : seats[0]) { v = 0; } will clear the stored values. Later, seats[1] = new int[6]; seats[1][0] = 5; is executed.
1. What are the types of `rowA` and `rowB`, and which of these assignments compile: `rowA = new int[3];`, `rowB = new int[3];`, `rowB = new int[3][4];`?
Read the worked answer
In int[] rowA, rowB[]; the brackets bind to each individual declarator, not to the shared base type.
rowAisint[]- it got the first pair of brackets.rowBisint[][]- it got one pair from the common type plus its own trailing pair.
Consequences:
rowA = new int[3];compiles (int[] to int[]).rowB = new int[3];does not compile: anint[]is not assignable toint[][].rowB = new int[3][4];compiles: a 3-by-4int[][].
rowB = new int[3][]; also compiles and creates three null rows, which is exactly the situation in seats.
The general rule: int[] a, b; makes both int[], while int a[], b; makes a an array and b a plain int. Placing brackets after the variable name changes only that variable.
2. Explain the NullPointerException in the for-each loop and the different failure of `for (int i = 0; i <= seats.length; i++)`. What is the minimal correct fix for each?
Read the worked answer
The for-each failure: new int[4][] allocates the outer array of length 4 whose element type is int[], and every element starts as null. Only indices 0 and 2 are replaced with real arrays. The for-each therefore hands out null for index 1 (and 3), and row.length dereferences a null reference, so NullPointerException is thrown on the first null row - index 1, before index 2 is ever visited.
Minimal fixes: null-guard (if (row != null) total += row.length;), skip by index, or make the array rectangular with new int[4][3], which allocates every row.
The index loop failure: the condition uses <= seats.length. With seats.length == 4, the loop runs for i = 0,1,2,3,4. At i = 4 the index is out of bounds and the JVM throws ArrayIndexOutOfBoundsException (a RuntimeException, no declaration needed). Even at i = 1 the null row would already have caused a NullPointerException, so the two bugs mask one another; the boundary bug is proven by changing the condition to <.
Minimal fixes: use i < seats.length and guard or initialise the null rows.
3. Does `for (int v : seats[0]) { v = 0; }` clear the array? Does `for (int[] row : seats) { row = new int[2]; }` replace rows? Explain, and state whether `seats[1] = new int[6]; seats[1][0] = 5;` is legal.
Read the worked answer
No to both modification attempts.
- For a primitive array, the for-each variable holds a copy of the element value. Assigning
v = 0only changes the local loop variable;seats[0]still holds 12, 14, 16. You must write by index:for (int i = 0; i < seats[0].length; i++) seats[0][i] = 0;or usejava.util.Arrays.fill(seats[0], 0);. - For the outer loop,
rowis a copy of the reference stored in the array slot. Assigningrow = new int[2]rebinds the local variable and leavesseats[i]untouched. To replace a row you must assign through the array:seats[i] = new int[2];. Note the asymmetry: mutating the object the reference points at (e.g.row[0] = 9;) does affect the array, because the copy points at the same object.
The last snippet is legal. seats[1] = new int[6]; installs a non-null row of length 6 (elements default to 0), and seats[1][0] = 5; then writes index 0 of that row. No exception occurs; the earlier null-row problem is fixed for index 1 only.