Java SE 8 Programmer I (OCAJP 8) study materials
Java SE 8 Programmer I (OCAJP 8) study resources

Seating Chart: Ragged Arrays, Enhanced For and Off-By-One Loops

Java SE 8 Programmer I (OCAJP 8) · Put your knowledge to work in context.

All case studies

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.

  • rowA is int[] - it got the first pair of brackets.
  • rowB is int[][] - 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: an int[] is not assignable to int[][].
  • rowB = new int[3][4]; compiles: a 3-by-4 int[][].

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 = 0 only 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 use java.util.Arrays.fill(seats[0], 0);.
  • For the outer loop, row is a copy of the reference stored in the array slot. Assigning row = new int[2] rebinds the local variable and leaves seats[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.