2.8. Arrays
An
array is a special kind of object that holds
zero or more primitive values or references. These values are held in
the elements of the array, which are unnamed variables
referred to by their position or index . The type
of an array is characterized by its element
type
, and all elements of the array must
be of that type.Array elements are numbered starting with zero, and valid
indexes
range from zero to the number of elements minus one. The array
element with index 1, for example, is the second
element in the array. The number of elements in an array is its
length . The length of an array is specified when
the array is created, and it never changes.The element type of an array may be any valid Java type, including
array types. This means that Java supports arrays of arrays, which
provide a kind of multidimensional array capability. Java does not
support the matrix-style multidimensional arrays found in some
languages.
2.8.1. Array Types
Array types
are reference types, just as classes are. Instances of arrays are
objects, just as the instances of a class are.[7]
Unlike classes, array types do not have to be defined. Simply place
square brackets after the element type.
For example, the following code declares three variables of array
type:
[7] There
is a terminology difficulty when discussing arrays. Unlike with
classes and their instances, we use the term
"array" for both the array type and
the array instance. In practice, it is usually clear from context
whether a type or a value is being discussed.
The length of an array is not part of the
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[]
String[] points; // String[] is an array of String objects
array type. It is not possible, for example, to declare a method that
expects an array of exactly four int values, for
example. If a method parameter is of type int[ ],
a caller can pass an array with any number (including zero) of
elements.Array types are not classes, but array instances are objects. This
means that arrays inherit the methods of
java.lang.Object. Arrays implement the
Cloneable
interface and
override the clone( ) method to guarantee that an
array can always be cloned and that clone( ) never
throws a CloneNotSupportedException. Arrays also
implement
Serializable so that
any array can be serialized if its element type can be serialized.
Finally, all arrays have a public final int field
named length that specifies the number of elements
in the array.
2.8.1.1 Array type widening conversions
Since arrays extend
Object and implement the
Cloneable and Serializable
interfaces, any array type can be widened to any of these three
types. But certain array types can also be widened to other array
types. If the element type of an array is a reference type
T, and T is assignable to a
type S, the array type T[ ] is
assignable to the array type S[ ]. Note that there
are no widening conversions of this sort for arrays of a given
primitive type. As examples, the following lines of code show legal
array widening conversions:
String[] arrayOfStrings; // Created elsewhereThis ability to widen an array type to another array type means that
int[][] arrayOfArraysOfInt; // Created elsewhere
// String is assignable to Object, so String[] is assignable to Object[]
Object[] oa = arrayOfStrings;
// String implements Comparable, so a String[] can be considered a Comparable[]
Comparable[] ca = arrayOfStrings;
// An int[] is an Object, so int[][] is assignable to Object[]
Object[] oa2 = arrayOfArraysOfInt;
// All arrays are cloneable, serializable Objects
Object o = arrayOfStrings;
Cloneable c = arrayOfArraysOfInt;
Serializable s = arrayOfArraysOfInt[0];
the compile-time type of an array is not always the same as its
runtime type. The compiler must usually insert runtime checks before
any operation that stores a reference value into an array element to
ensure that the runtime type of the value matches the runtime type of
the array element. If the runtime check fails, an
ArrayStoreException
is thrown.
2.8.1.2 C compatibility syntax
As we've seen, an array type is written simply by
placing brackets after the element type. For compatibility with
C
and C++, however, Java supports an alternative syntax in variable
declarations: brackets may be placed after the name of the variable
instead of, or in addition to, the element type. This applies to
local variables, fields, and method parameters. For example:
// This line declares local variables of type int, int[] and int[][]This compatibility syntax is uncommon, and its use is strongly
int justOne, arrayOfThem[], arrayOfArrays[][];
// These three lines declare fields of the same array type:
public String[][] aas1; // Preferred Java syntax
public String aas2[][]; // C syntax
public String[] aas3[]; // Confusing hybrid syntax
// This method signature includes two parameters with the same type
public static double dotProduct(double[] x, double y[]) { ... }
discouraged.
2.8.2. Creating and Initializing Arrays
To create an array value in Java,
you use the new
keyword, just as you do to create an object. Array types
don't have constructors, but you are required to
specify a length whenever you create an array.
Specify the desired size of your array as a nonnegative integer
between square brackets:
byte[] buffer = new byte[1024]; // Create a new array to hold 1024 bytesWhen you create an array with this syntax, each of the array elements
String[] lines = new String[50]; // Create an array of 50 references to strings
is automatically initialized to the same default value that is used
for the fields of a class: false for
boolean elements, '\u0000' for
char elements, 0 for integer elements, 0.0 for
floating-point elements, and null for elements of
reference type.Array creation expressions can also be used to create and initialize
a multidimensional rectangular array of arrays. This syntax is
somewhat more complicated and is explained later in this section.
2.8.2.1 Array initializers
To
create an array and initialize its elements in a single expression,
omit the array length and follow the square brackets with a
comma-separated list of expressions
within curly braces. The type of each expression must be assignable
to the element type of the array, of course. The length of the array
that is created is equal to the number of expressions. It is legal,
but not necessary, to include a trailing comma following the last
expression in the list. For example:
String[] greetings = new String[] { "Hello", "Hi", "Howdy" };Note that this syntax allows arrays to be created, initialized, and
int[] smallPrimes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, };
used without ever being assigned to a variable. In a sense these
array creation expressions are anonymous array literals. Here are
examples:
// Call a method, passing an anonymous array literal that contains two stringsWhen an array initializer is part of a variable declaration, you may
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) });
omit the new keyword and element type and list the
desired array elements within curly braces:
String[] greetings = { "Hello", "Hi", "Howdy" };The Java Virtual Machine
int[] powersOfTwo = {1, 2, 4, 8, 16, 32, 64, 128};
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];If you want to initialize a large array, you should think twice
perfectNumbers[0] = 6;
perfectNumbers[1] = 28;
before including the values literally in the program, since the Java
compiler has to emit lots of Java byte codes to initialize the array.
It may be more space-efficient to store your data in an external file
and read it into the program at runtime.The fact that Java does all array initialization at runtime has an
important corollary, however. It means that the expressions in an
array initializer may be computed at runtime and need not be
compile-time constants. For example:
Point[] points = { circle1.getCenterPoint( ), circle2.getCenterPoint( ) };
2.8.3. Using Arrays
Once an array has been created, you are ready to start using it. The
following sections explain basic access to the elements of an array
and cover common idioms of array usage such as iterating through the
elements of an array and copying an array or part of an array.
2.8.3.1 Accessing array elements
The
elements of an array are variables. When an array element appears in
an expression, it evaluates to the value held in the element. And
when an array element appears on the left-hand side of an assignment
operator, a new value is stored into that element. Unlike a normal
variable, however, an array element has no name, only a number. Array
elements are accessed using a square bracket notation. If
a is an expression that evaluates to an array
reference, you index that array and refer to a specific element with
a[i], where i is an integer
literal or an expression that evaluates to an int.
For example:
String[] responses = new String[2]; // Create an array of two stringsThe array index expression must be of type int, or
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] + " ): ");
// Both the array reference and the array index may be more complex expressions
double datum = data.getMatrix( )[data.row( )*data.numColumns( ) +
data.column( )];
a type that can be widened to an int:
byte, short, or even
char. It is obviously not legal to index an array
with a boolean, float, or
double value. Remember that the
length field of an array is an
int and that arrays may not have more than
Integer.MAX_VALUE elements. Indexing an array with
an expression of type long generates a
commpile-time error, even if the value of that expression at runtime
would be within the range of an int.
2.8.3.2 Array bounds
Remember
that the first element of an array a is
a[0] , the second element is
a[1] and the last is
a[a.length-1]. If you are accustomed to a language
in which the arrays are 1-based, 0-based arrays take some getting
used to.A common bug involving arrays is use of an
index that is too small (a
negative index) or too large (greater than or equal to the array
length). In languages like C or
C++, accessing elements before the beginning or after the end of an
array yields unpredictable behavior that can vary from invocation to
invocation and platform to platform. Such bugs may not always be
caught, and if a failure occurs, it may be at some later time. While
it is just as easy to write faulty array indexing code in Java, Java
guarantees predictable results by checking every array access at
runtime. If an array index is too small or too large, Java throws an
ArrayIndexOutOfBoundsException
immediately.
2.8.3.3 Iterating arrays
It
is common to write loops that iterate through each of the elements of
an array in order to perform some operation on it. This is typically
done with a for loop. The following code, for example,
computes the sum of an array of integers:
int[] primes = { 2, 3, 5, 7, 11, 13, 17, 19 };The structure of this for loop is idiomatic, and
int sumOfPrimes = 0;
for(int i = 0; i < primes.length; i++)
sumOfPrimes += primes[i];
you'll see it frequently.In Java 5.0 and later, arrays can also be iterated with the
for/in loop. The summing code could be
rewritten succinctly as follows:
for(int p : primes) sumOfPrimes += p;
2.8.3.4 Copying arrays
All
array
types implement the
Cloneable interface, and any array can be copied by
invoking its clone( ) method. Note that a cast is
required to convert the return value to the appropriate array type,
but that the clone( ) method of arrays is
guaranteed not to throw
CloneNotSupportedException:
int[] data = { 1, 2, 3 };The clone( ) method makes a shallow copy. If the
int[] copy = (int[]) data.clone( );
element type of the array is a reference type, only the references
are copied, not the referenced objects themselves. Because the copy
is shallow, any array can be cloned, even if the element type is not
itself Cloneable.Sometimes you simply want to copy elements from one existing array to
another existing array. The System.arraycopy(
)
method is designed to do this efficiently, and you can assume that
Java VM implementations performs this method using high-speed block
copy operations on the underlying hardware.arraycopy( ) is a straightforward function that is
difficult to use only because it has five arguments to remember.
First pass the source array from which elements are to be copied.
Second, pass the index of the start element in that array. Pass the
destination array and the destination index as the third and fourth
arguments. Finally, as the fifth argument, specify the number of
elements to be copied.arraycopy( ) works correctly even for overlapping
copies within the same array. For example, if you've
"deleted" the element at index
0 from array a and want to
shift the elements between indexes 1 and
n down one so that they occupy indexes
0 through n-1 you could do
this:
System.arraycopy(a, 1, a, 0, n);
2.8.3.5 Array utilities
The
java.util.Arrays class contains a number of static
utility methods for working with arrays. Most of these methods are
heavily overloaded, with versions for arrays of each primitive type
and another version for arrays of objects. The sort(
) and
binarySearch( ) methods are particularly useful
for sorting and
searching
arrays. The equals( ) method
allows you to compare the content of two arrays. The
Arrays.toString( ) method is
useful when you want to convert array content to a string, such as
for debugging or logging output.As of Java 5.0, the Arrays class includes
deepEquals( ) , deepHashCode( ),
and deepToString( ) methods that work correctly
for multidimensional arrays.
2.8.4. Multidimensional Arrays
As we've seen, an array
type is written as 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, you need to understand a few
additional details.Imagine that you want to
use a multidimensional array to represent a multiplication table:
int[][] products; // A multiplication tableEach 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 10 int[] valuesThe
for(int i = 0; i < 10; i++) // Loop 10 times...
products[i] = new int[10]; // ...and create 10 arrays
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][][];The first line creates a single-dimensional array, where each element
float[][][] globalTemperatureData = new float[360][180][];
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!Like a one-dimensional array, a
float[][][] globalTemperatureData = new float[][180][100]; // Error!
multidimensional array can be initialized using an array initializer.
Simply use nested sets of curly braces to nest arrays within arrays.
For example, we can declare, create, and initialize a 55
multiplication table like this:
int[][] products = { {0, 0, 0, 0, 0},Or, if you want to use a multidimensional array without declaring a
{0, 1, 2, 3, 4},
{0, 2, 4, 6, 8},
{0, 3, 6, 9, 12},
{0, 4, 8, 12, 16} };
variable, you can use the anonymous initializer syntax:
boolean response = bilingualQuestion(question, new String[][] {When you create a multidimensional array
{ "Yes", "No" },
{ "Oui", "Non" }});
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 matrices. 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 diagonally from top left to
bottom right, we can represent the same information in a
nonrectangular array with fewer elements:
int[][] products = { {0},When
{0, 1},
{0, 2, 4},
{0, 3, 6, 9},
{0, 4, 8, 12, 16} };
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.
}