home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  

Book Home Java Enterprise in a Nutshell Search this book

2.9. Array Types

Array types are the second kind of reference types in Java. An array is an ordered collection, or numbered list, of values. The values can be primitive values, objects, or even other arrays, but all of the values in an array must be of the same type. The type of the array is the type of the values it holds, followed by the characters []. For example:

byte b;                        // byte is a primitive type
byte[] arrayOfBytes;           // byte[] is an array type: array of byte
byte[][] arrayOfArrayOfBytes;  // byte[][] is another type: array of byte[]
Point[] points;                // Point[] is an array of Point objects

For compatibility with C and C++, Java also supports another syntax for declaring variables of array type. In this syntax, one or more pairs of square brackets follow the name of the variable, rather than the name of the type:

byte arrayOfBytes[];            // Same as byte[] arrayOfBytes
byte arrayOfArrayOfBytes[][];   // Same as byte[][] arrayOfArrayOfBytes
byte[] arrayOfArrayOfBytes[];   // Ugh! Same as byte[][] arrayOfArrayOfBytes

This is almost always a confusing syntax, however, and it is not recommended.

With classes and objects, we have separate terms for the type and the values of that type. With arrays, the single word array does double duty as the name of both the type and the value. Thus, we can speak of the array type int[] (a type) and an array of int (a particular array value). In practice, it is usually clear from context whether a type or a value is being discussed.

2.9.1. Creating Arrays

To create an array value in Java, you use the new keyword, just as you do to create an object. Arrays don't need to be initialized like objects do, however, so you don't pass a list of arguments between parentheses. What you must specify, though, is how big you want the array to be. If you are creating a byte[], for example, you must specify how many byte values you want it to hold. Array values have a fixed size in Java. Once an array is created, it can never grow or shrink. Specify the desired size of your array as a non-negative integer between square brackets:

byte[] buffer = new byte[1024];
String[] lines = new String[50];

When you create an array with this syntax, each of the values held in the array is automatically initialized to its default value. This is false for boolean values, '\u0000' for char values, 0 for integer values, 0.0 for floating-point values, and null for objects or array values.

2.9.2. Using Arrays

Once you've created an array with the new operator and the square-bracket syntax, you also use square brackets to access the individual values contained in the array. Remember that an array is an ordered collection of values. The elements of an array are numbered sequentially, starting with 0. The number of an array element refers to the element. This number is often called the index, and the process of looking up a numbered value in an array is sometimes called indexing the array.

To refer to a particular element of an array, simply place the index of the desired element in square brackets after the name of the array. For example:

String[] responses = new String[2];   // Create an array of two strings
responses[0] = "Yes";                 // Set the first element of the array
responses[1] = "No";                  // Set the second element of the array

// Now read these array elements
System.out.println(question + " (" + responses[0] + "/" +
                   responses[1] + " ): ");

In some programming languages, such as C and C++, it is a common bug to write code that tries to read or write array elements that are past the end of the array. Java does not allow this. Every time you access an array element, the Java interpreter automatically checks that the index you have specified is valid. If you specify a negative index or an index that is greater than the last index of the array, the interpreter throws an exception of type ArrayIndexOutOfBoundsException. This prevents you from reading or writing nonexistent array elements.

Array index values are integers; you cannot index an array with a floating-point value, a boolean, an object, or another array. char values can be converted to int values, so you can use characters as array indexes. Although long is an integer data type, long values cannot be used as array indexes. This may seem surprising at first, but consider that an int index supports arrays with over two billion elements. An int[] with this many elements would require eight gigabytes of memory. When you think of it this way, it is not surprising that long values are not allowed as array indexes.

Besides setting and reading the value of array elements, there is one other thing you can do with an array value. Recall that whenever we create an array, we must specify the number of elements the array holds. This value is referred to as the length of the array; it is an intrinsic property of the array. If you need to know the length of the array, append .length to the array name:

if (errorCode < errorMessages.length)

.length is special Java syntax for arrays. An expression like a.length looks as though it refers to a field of an object a, but this is not actually the case. The .length syntax can be used only to read the length of an array. It cannot be used to set the length of an array (because, in Java, an array has a fixed length that can never change).

In the previous example, the array index within square brackets is a variable, not an integer literal. In fact, arrays are most often used with loops, particularly for loops, where they are indexed using a variable that is incremented or decremented each time through the loop:

int[] values;                          // Array elements initialized elsewhere
int total = 0;                         // Store sum of elements here
for(int i = 0; i < values.length; i++) // Loop through array elements
  total += values[i];                  // Add them up

In Java, the first element of an array is always element number 0. If you are accustomed to a programming language that numbers array elements beginning with 1, this will take some getting used to. For an array a, the first element is a[0], the second element is a[1], and the last element is:

a[a.length - 1]         // The last element of any array named a

2.9.3. Array Literals

The null literal used to represent the absence of an object can also be used to represent the absence of an array. For example:

char[] password = null;

In addition to the null literal, Java also defines special syntax that allows you to specify array values literally in your programs. There are actually two different syntaxes for array literals. The first, and more commonly used, syntax can be used only when declaring a variable of array type. It combines the creation of the array object with the initialization of the array elements:

int[] powersOfTwo = {1, 2, 4, 8, 16, 32, 64, 128};

This creates an array that contains the eight int elements listed within the curly braces. Note that we don't use the new keyword or specify the type of the array in this array literal syntax. The type is implicit in the variable declaration of which the initializer is a part. Also, the array length is not specified explicitly with this syntax; it is determined implicitly by counting the number of elements listed between the curly braces. There is a semicolon following the close curly brace in this array literal. This is one of the fine points of Java syntax. When curly braces delimit classes, methods, and compound statements, they are not followed by semicolons. However, for this array literal syntax, the semicolon is required to terminate the variable declaration statement.

The problem with this array literal syntax is that it works only when you are declaring a variable of array type. Sometimes you need to do something with an array value (such as pass it to a method) but are going to use the array only once, so you don't want to bother assigning it to a variable. In Java 1.1 and later, there is an array literal syntax that supports this kind of anonymous arrays (so called because they are not assigned to variables, so they don't have names). This kind of array literal looks as follows:

// Call a method, passing an anonymous array literal that contains two strings
String response = askQuestion("Do you want to quit?",
                              new String[] {"Yes", "No"});

// Call another method with an anonymous array (of anonymous objects)
double d = computeAreaOfTriangle(new Point[] { new Point(1,2),
                                               new Point(3,4),
                                               new Point(3,2) });

With this syntax, you use the new keyword and specify the type of the array, but the length of the array is not explicitly specified.

It is important to understand that the Java Virtual Machine architecture does not support any kind of efficient array initialization. In other words, array literals are created and initialized when the program is run, not when the program is compiled. Consider the following array literal:

int[] perfectNumbers = {6, 28};

This is compiled into Java byte codes that are equivalent to:

int[] perfectNumbers = new int[2];
perfectNumbers[0] = 6;
perfectNumbers[1] = 28;

Thus, if you want to include a large amount of data in a Java program, it may not be a good idea to include that data literally in an array, since the Java compiler has to create lots of Java byte codes to initialize the array, and then the Java interpreter has to laboriously execute all that initialization code. In cases like this, it is better to store your data in an external file and read it into the program at runtime.

The fact that Java does all array initialization explicitly at runtime has an important corollary, however. It means that the elements of an array literal can be arbitrary expressions that are computed at runtime, rather than constant expressions that are resolved by the compiler. For example:

Point[] points = { circle1.getCenterPoint(), circle2.getCenterPoint() };

2.9.4. Multidimensional Arrays

As we've seen, an array type is simply the element type followed by a pair of square brackets. An array of char is char[], and an array of arrays of char is char[][]. When the elements of an array are themselves arrays, we say that the array is multidimensional. In order to work with multidimensional arrays, there are a few additional details you must understand.

Imagine that you want to use a multidimensional array to represent a multiplication table:

int[][] products;      // A multiplication table

Each of the pairs of square brackets represents one dimension, so this is a two-dimensional array. To access a single int element of this two-dimensional array, you must specify two index values, one for each dimension. Assuming that this array was actually initialized as a multiplication table, the int value stored at any given element would be the product of the two indexes. That is, products[2][4] would be 8, and products[3][7] would be 21.

To create a new multidimensional array, use the new keyword and specify the size of both dimensions of the array. For example:

int[][] products = new int[10][10];

In some languages, an array like this would be created as a single block of 100 int values. Java does not work this way. This line of code does three things:

  • Declares a variable named products to hold an array of arrays of int.

  • Creates a 10-element array to hold 10 arrays of int.

  • Creates 10 more arrays, each of which is a 10-element array of int. It assigns each of these 10 new arrays to the elements of the initial array. The default value of every int element of each of these 10 new arrays is 0.

To put this another way, the previous single line of code is equivalent to the following code:

int[][] products = new int[10][];    // An array to hold ten int[] values. 
for(int i = 0; i < 10; i++)          // Loop ten times... 
  products[i] = new int[10];         // ...and create ten arrays. 

The new keyword performs this additional initialization automatically for you. It works with arrays with more than two dimensions as well:

float[][][] globalTemperatureData = new float[360][180][100];

When using new with multidimensional arrays, you do not have to specify a size for all dimensions of the array, only the leftmost dimension or dimensions. For example, the following two lines are legal:

float[][][] globalTemperatureData = new float[360][][];
float[][][] globalTemperatureData = new float[360][180][];

The first line creates a single-dimensional array, where each element of the array can hold a float[][]. The second line creates a two-dimensional array, where each element of the array is a float[]. If you specify a size for only some of the dimensions of an array, however, those dimensions must be the leftmost ones. The following lines are not legal:

float[][][] globalTemperatureData = new float[360][][100];  // Error!
float[][][] globalTemperatureData = new float[][180][100];  // Error!

Like a one-dimensional array, a multidimensional array can be initialized using an array literal. Simply use nested sets of curly braces to nest arrays within arrays. For example, we can declare, create, and initialize a 5×5 multiplication table like this:

int[][] products = { {0, 0, 0, 0, 0},
                     {0, 1, 2, 3, 4},
                     {0, 2, 4, 6, 8},
                     {0, 3, 6, 9, 12},
                     {0, 4, 8, 12, 16} };

Or, if you want to use a multidimensional array without declaring a variable, you can use the anonymous initializer syntax:

boolean response = bilingualQuestion(question, new String[][] {
                                                   { "Yes", "No" },
                                                   { "Oui", "Non" }});

When you create a multidimensional array using the new keyword, you always get a rectangular array: one in which all the array values for a given dimension have the same size. This is perfect for rectangular data structures, such as matrixes. However, because multidimensional arrays are implemented as arrays of arrays in Java, instead of as a single rectangular block of elements, you are in no way constrained to use rectangular arrays. For example, since our multiplication table is symmetrical about the diagonal from top left to bottom right, we can represent the same information in a nonrectangular array with fewer elements:

int[][] products = { {0},
                     {0, 1},
                     {0, 2, 4},

                     {0, 3, 6, 9},
                     {0, 4, 8, 12, 16} };

When working with multidimensional arrays, you'll often find yourself using nested loops to create or initialize them. For example, you can create and initialize a large triangular multiplication table as follows:

int[][] products = new int[12][];          // An array of 12 arrays of int.
for(int row = 0; row < 12; row++) {        // For each element of that array,
  products[row] = new int[row+1];          // allocate an array of int. 
  for(int col = 0; col < row+1; col++)     // For each element of the int[],
    products[row][col] = row * col;        // initialize it to the product. 

Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.