Section 7.4 "Multidimensional" Arrays
Subsection 7.4.1 Declaring Arrays of Arrays
In Section 7.1 we learned that we could declare an array variable of any Java type by following the type with a pair of square brackets (
[]
). Furthermore, an array with elements of a data type is created using the the new
keyword followed by the type and a pair of square brackets with a number of elements.In the following code snippet the
list
variable is declared with type double array, and initialized with a new double array of size 5. We follow the rules outlined above to create and initialize a new array that itself holds arrays. If double[]
is the type of a double array formed by adding a pair of square brackets to the right of the type double
, then an array of double arrays is obtained by adding another pair of square brackets after the double array, like double[][]
. Continuing with this line of reasoning, if the expression new double[5]
creates a new double
array with 5 items, then the expression new double[5][3]
creates a new array capable of holding 5 double
arrays, each of which can hold 3 doubles. This is demonstrated in the second half of the following code snippet and illustrated in Figure 7.4.1. This same pattern of adding square brackets may be continued to form arrays of arrays of arrays, etc.// An array of doubles
double[] list = new double[5];
// An array of arrays of doubles
double[][] matrix = new double[5][3];
matrix
, an array of arraysSubsection 7.4.2 Accessing "Multidimensional" Array Elements
Using the same reasoning as in Subsection 5.6.1 we can access all elements in a "multidimensional" array using nested iteration. We use an outer loop with a loop counter to generate the indexes of the elements in the top-level array, and we use an inner loop with a counter to generate the indexes of the elements in each inner array. It is best to use the arrayβs
.length
property in the continuation test of both the outer and inner loops.
Listing 7.4.2 demonstrates these ideas. This program has two methods defined,
main(β¦)
and print2D(β¦)
. Both methods use the nested iteration approach to access array elements. In main(β¦)
we access each element and initialize values to random doubles. In print2D(β¦)
we access each element for printing. Pay special attention to the inner loop continuation test, j<arr2D[i].length
. We are querying each inner array for its length, that is, each array at arr2D[i]
. In the next section weβll learn why we want to ask each inner array for its length, and not use the length of just the first inner array or a constant.// NestedArrays1.java
import java.util.Random;
public class NestedArrays1 {
public static void main(String[] args) {
Random rnd = new Random(); // Random object
double[][] arr2D = new double[5][3]; // 2D array
// Fill array with random numbers
for (int i=0; i<arr2D.length; i++) { // Outer loop
for (int j=0; j<arr2D[i].length; j++) { // Inner loop
arr2D[i][j] = rnd.nextDouble();
}
}
print2D( arr2D ); // Print array
}
// Print an arbitrary 2D double array
public static void print2D( double[][] arr ) {
for (int i=0; i<arr.length; i++) { // Outer loop
for (int j=0; j<arr[i].length; j++) { // Inner loop
System.out.print( arr[i][j] + " "); // Print adjacent
}
System.out.println(); // Next line
}
}
}
NestedArrays1.java
javac NestedArrays1.java java NestedArrays1 0.503106975940591 0.9917565242540053 0.17632812964595546 0.7495600013315958 0.01605752178519637 0.9485705200206167 0.33889267773442544 0.47985018921768474 0.042949682032970005 0.6807634494160301 0.5147152407456702 0.312225944100627 0.7609294164182726 0.21161251517956636 0.890812892598448
Initialization of multidimensional arrays is permitted at the time of declaration using curly braces in a manner similar to initializing an array with one dimension. For multidimensional arrays the initialization structure must match the structure of the array, using nested curly braces
{{β¦}, {β¦}, β¦}
.In Listing 7.4.3 we rewrite the example from Listing 7.4.2, only this time we initialize the array at declaration time, and we iterate over all nested array elements using a for-each statement instead of a for-statement.
Note how we use nested curly braces to initialize the array, and how we donβt specify the dimensions of the nested arrays. Java determines the array dimensions by the initialization statement structure. Also note the type of the variable in the outer for-each loop inside
printArr(β¦)
, which is for (double[] inarr : arr) {β¦
. The loop variable is a reference to a one-dimensional double array! By now, this should make sense. In Java, a "multidimensional" array is an array of arrays; the outer array is a one-dimensional array with elements that each hold one-dimensional arrays.// NestedArrays2.java
public class NestedArrays2 {
public static void main(String[] args) {
double[][] arr2D = {{0.0, 0.1, 0.2}, // 2D array
{1.0, 1.1, 1.2}, // Initialization
{2.0, 2.1, 2.2},
{3.0, 3.1, 3.2},
{4.0, 4.1, 4.2}};
print2D( arr2D ); // Print array
}
// Print an arbitrary 2D double array
public static void print2D( double[][] arr ) {
for (double[] inarr : arr) { // Outer loop
for (double val : inarr) { // Inner loop
System.out.print( val + " "); // Print adjacent
}
System.out.println(); // Next line
}
}
}
NestedArrays2.java
javac NestedArrays2.java java NestedArrays2 0.0 0.1 0.2 1.0 1.1 1.2 2.0 2.1 2.2 3.0 3.1 3.2 4.0 4.1 4.2
Subsection 7.4.3 Ragged Arrays
In Listing 7.4.3 we initialized our multidimensional array explicitly using nested curly braces
{{β¦}, {β¦}, β¦}
. Array dimensions were determined using the structure of the initializer. This begs the question, what would happen if we initialized the array using something like the following, an initializer without uniform inner dimensions? Would it work?double[][] arr2D = {{0.0, 0.1, 0.2}, // Right side is "ragged"
{1.0, 1.1, 1.2, 1.3},
{2.0, 2.1},
{3.0, 3.1, 3.2},
{4.0}};
If itβs true that Javaβs multidimensional arrays are really just arrays of arrays, then this syntax should be valid. There should be no constraint on the lengths of the inner arrays because outer array elements hold array references, which donβt care about the length of the arrays that they reference.
Listing 7.4.4 is the program in Listing 7.4.3 only with a ragged initializer. Compiling and running this program produces no errors, and the expected output. This demonstrates that it is possible to have internal arrays with unequal lengths.
// NestedArrays2.java
public class NestedArrays3 {
public static void main(String[] args) {
double[][] arr2D = {{0.0, 0.1, 0.2}, // Right side is "ragged"
{1.0, 1.1, 1.2, 1.3},
{2.0, 2.1},
{3.0, 3.1, 3.2},
{4.0}};
print2D( arr2D ); // Print array
}
// Print an arbitrary 2D double array
public static void print2D( double[][] arr ) {
for (double[] inarr : arr) { // Outer loop
for (double val : inarr) { // Inner loop
System.out.print( val + " "); // Print adjacent
}
System.out.println(); // Next line
}
}
}
NestedArrays3.java
javac NestedArrays3.java java NestedArrays3 0.0 0.1 0.2 1.0 1.1 1.2 1.3 2.0 2.1 3.0 3.1 3.2 4.0
Multidimensional arrays with unequal lengths are called ragged arrays because they appear to have ragged right edges when printed. A schematic of the
arr2D
array is illustrated in Figure 7.4.5.arr2D
, a ragged 2D arrayMultidimensional arrays in Java can even be assembled incrementally. Have a close look at our final example in Listing 7.4.6. There are several important features to note.
// RaggedAssembly.java
// Assemble a ragged array one inner array at a time.
public class RaggedAssembly {
public static void main(String[] args) {
double[][] arr2D = new double[5][]; // Inner lengths unspecified
// Create and assign all inner arrays using various approaches
arr2D[0] = new double[] {0.0, 0.1, 0.2};
arr2D[1] = new double[] {1.0, 1.1, 1.2, 1.3};
arr2D[2] = new double[3];
//arr2D[3] = // Leave unassigned
arr2D[4] = new double[] {4.0};
print2D( arr2D ); // // Print array
}
// Print an arbitrary 2D double array
public static void print2D( double[][] arr ) {
for (double[] inarr : arr) { // Outer loop
if (inarr != null) { // Check array exists
for (double val : inarr) { // Inner loop
System.out.print( val + " "); // Print adjacent
}
System.out.println(); // Next line
} else {
System.out.println(inarr); // Null reference
}
}
}
}
RaggedAssembly.java
javac RaggedAssembly.java java RaggedAssembly 0.0 0.1 0.2 1.0 1.1 1.2 1.3 0.0 0.0 0.0 null 4.0
When we create the
arr2D
array in Listing 7.4.6 we leave out the size of the inner dimension (new double[5][]
). This is an important step. It signals to Java that no inner arrays should be created. Immediately after the arr2D
array is created, all element values will be null
.We initialize each element of
arr2D
one at a time, using different techniques. For the first, second, and last elements, we create and initialize one-dimensional arrays and assign them to arr2D
array elements. For the third element, we create but do not initialize a one-dimensional array and assign it. As we know, the values of an uninitialized array with a numeric type will be 0. We leave the fourth array element unassigned. This leaves the value of arr2D[3]
as null
. All these options are permitted, because we have the flexibility of arr2D
being an array of array references.One final item to note is the update we made to the
print2D(β¦)
method. Because our internal array references can be null
, we need to test for this case. After all, the outer array is just an array of references, which can have the value null
. To avoid a runtime error that would occur if we tried to print arr2D
with an unassigned element reference, we add an if-statement to the print2D(β¦)
that checks that each inner array reference is not null
before attempting to print its elements. If it is null
, the method just goes ahead and prints the element. Looking at the output from compiling and running Listing 7.4.6, you will see that this all checks out.