An array is a special type of object that can hold an ordered collection of elements. The type of the elements of the array is called the base type of the array; the number of elements it holds is a fixed attribute called its length . Java supports arrays of all primitive and reference types.
The basic syntax of arrays looks much like that of C or C++. We
create an array of a specified length and access the elements with
the
index
operator, []
. Unlike other languages, however,
arrays in Java are true, first-class objects. An array is an instance
of a special Java array class and has a corresponding type in the
type system. This means that to use an array, as with any other
object, we first declare a variable of the appropriate type and then
use the new
operator to create an instance of it.
Array objects differ from other objects in Java in three respects:
Java implicitly creates a special array class for us whenever we declare an arraytype variable. It’s not strictly necessary to know about this process in order to use arrays, but it helps in understanding their structure and their relationship to other objects in Java.
Java lets us use the special
[]
operator to access array elements, so that arrays look as we expect. We could implement our own classes that act like arrays, but because Java doesn’t have user-defined operator overloading, we would have to settle for having methods likeget( )
andput( )
instead of using the special[]
notation.Java provides a corresponding special form of the
new
operator that lets us construct an instance of an array and specify its length with the[]
notation.
An array-type variable is
denoted
by a base type followed by the empty brackets, []
.
Alternatively, Java accepts a C-style declaration, with the
brackets placed after the array name.
int [] arrayOfInts; int arrayOfInts [];
In each case, arrayOfInts
is declared as an array
of integers. The size of the array is not yet an issue, because we
are declaring only the array-type variable. We have not yet created
an actual instance of the array class, with its associated storage.
It’s not even possible to specify the length of an array when
creating an array-type variable.
An array of objects can be created in the same way:
String [] someStrings; Button someButtons [];
The new
operator is used to create an instance of
an
array.
After the new
operator, we specify the base type
of the array and its length, with a bracketed integer expression:
arrayOfInts = new int [42]; someStrings = new String [ number + 2 ];
We can, of course, combine the steps of declaring and allocating the array:
double [] someNumbers = new double [20]; Component widgets [] = new Component [12];
As in C, array indices start with zero.
Thus, the first element of someNumbers[]
is
0
and the last element is 19
.
After creation, the array elements are initialized to the default
values for their type. For numeric types, this means the elements are
initially zero:
int [] grades = new int [30]; grades[0] = 99; grades[1] = 72; // grades[2] == 0
The elements of an array of objects are references to the objects,
not actual instances of the objects. The default value of each
element is therefore null
, until we assign
instances of appropriate objects:
String names [] = new String [4]; names [0] = new String( ); names [1] = "Boofa"; names [2] = someObject.toString( ); // names[3] == null
This is an important distinction that can cause confusion. In many
other languages, the act of creating an array is the same as
allocating storage for its elements. In Java, a newly allocated array
of objects actually contains only reference variables, each with the
value null
.[16] That’s not to say that there is no memory
associated with an empty array—there is memory needed to hold
those references (the empty “slots” in the array). Figure 4.4 illustrates the names
array of the previous example.
names
is a variable of type
String[]
(i.e., a string array). This particular
String[]
object contains four
String
type variables. We have assigned
String
objects to the first three array elements.
The fourth has the default value null
.
Java supports the C-style
curly braces {}
construct for creating an array and initializing its elements:
int [] primes = { 1, 2, 3, 5, 7, 7+4 }; // primes[2] == 3
An array object of the proper type and length is implicitly created and the values of the comma-separated list of expressions are assigned to its elements.
We can use the {}
syntax with an array of objects.
In this case, each of the expressions must evaluate to an object that
can be assigned to a variable of the base type of the array, or the
value null
. Here are some examples:
String [] verbs = { "run", "jump", someWord.toString( ) }; Button [] controls = { stopButton, new Button("Forwards"), new Button("Backwards") }; // All types are subtypes of Object Object [] objects = { stopButton, "A word", null };
Button [] threeButtons = new Button [3]; Button [] threeButtons = { null, null, null };
The
size of an array object is available in the public variable
length
:
char [] alphabet = new char [26]; int alphaLen = alphabet.length; // alphaLen == 26 String [] musketeers = { "one", "two", "three" }; int num = musketeers.length; // num == 3
length
is the only accessible field of an array;
it is a variable, not a method. (Don’t worry, the compiler will
tell you when you accidentally put those parentheses on, as if it
were a method; everyone does now and then.)
Array access in Java is just like array access in C; you access an
element by putting an integer-valued expression between brackets
after the name of the array. The following example creates an array
of Button
objects called keyPad
and then fills the array with Button
objects:
Button [] keyPad = new Button [ 10 ]; for ( int i=0; i < keyPad.length; i++ ) keyPad[ i ] = new Button( Integer.toString( i ) );
Attempting
to access an element that is outside the range of the array generates
an ArrayIndexOutOfBoundsException
. This is a type
of RuntimeException
, so you can either catch and
handle it yourself, or ignore it, as we’ve already discussed:
String [] states = new String [50]; try { states[0] = "California"; states[1] = "Oregon"; ... states[50] = "McDonald's Land"; // Error: array out of bounds } catch ( ArrayIndexOutOfBoundsException err ) { System.out.println( "Handled error: " + err.getMessage( ) ); }
It’s a common task to
copy a range of elements from one array into another. Java supplies
the arraycopy( )
method for this purpose;
it’s a utility method of the System
class:
System.arraycopy(source,sourceStart,destination,destStart,length);
The following example doubles the size of the
names
array from an earlier example:
String [] tmpVar = new String [ 2 * names.length ]; System.arraycopy( names, 0, tmpVar, 0, names.length ); names = tmpVar;
A new array, twice the size of names
, is allocated
and assigned to a temporary variable tmpVar
.
arraycopy( )
is used to copy the elements of
names
to the new array. Finally, the new array is
assigned to names
. If there are no remaining
references to the old array object after names
has
been copied, it will be garbage-collected on the next pass.
You often want to create “throw-away” arrays: arrays that are only used in one place and never referenced anywhere else. Such arrays don’t need to have a name, because you never need to refer to them again in that context. For example, you may want to create a collection of objects to pass as an argument to some method. It’s easy enough to create a normal, named array—but if you don’t actually work with the array (if you use the array only as a holder for some collection), you shouldn’t have to. Java makes it easy to create “anonymous” (i.e., unnamed) arrays.
Let’s say you need to call a method named setPets( )
, which takes an array of Animal
objects as arguments. Cat
and
Dog
are subclasses of Animal
.
Here’s how to call setPets( )
using an
anonymous array:
Dog pokey = new Dog ("gray"); Cat squiggles = new Cat ("black"); Cat jasmine = new Cat ("orange"); setPets ( new Animal [] { pokey, squiggles, jasmine });
The syntax looks just like the initialization of an array in a
variable declaration. We implicitly define the size of the array and
fill in its elements using the curly brace notation. However, since
this is not a variable declaration, we have to explicitly use the
new
operator to create the array object.
You can use anonymous arrays to simulate variable-length argument lists (called VARARGS in C), a feature of many programming languages that Java doesn’t provide. The advantage of anonymous arrays over variable-length argument lists is that the former allow stricter type checking; the compiler always knows exactly what arguments are expected, and therefore it can verify that method calls are correct.
Java supports multidimensional arrays in the form of arrays of array type objects. You create a multidimensional array with C-like syntax, using multiple bracket pairs, one for each dimension. You also use this syntax to access elements at various positions within the array. Here’s an example of a multidimensional array that represents a chess board:
ChessPiece [][] chessBoard; chessBoard = new ChessPiece [8][8]; chessBoard[0][0] = new ChessPiece( "Rook" ); chessBoard[1][0] = new ChessPiece( "Pawn" ); ...
Here chessBoard
is declared as a variable of type
ChessPiece[][]
(i.e., an array of
ChessPiece
arrays). This declaration implicitly
creates the type ChessPiece[]
as well. The example
illustrates the special form of the new
operator
used to create a multidimensional array. It creates an array of
ChessPiece[]
objects and then, in turn, creates
each array of ChessPiece
objects. We then index
chessBoard
to specify values for particular
ChessPiece
elements. (We’ll neglect the
color of the pieces here.)
Of course, you can create arrays with more than two dimensions. Here’s a slightly impractical example:
Color [][][] rgbCube = new Color [256][256][256]; rgbCube[0][0][0] = Color.black; rgbCube[255][255][0] = Color.yellow; ...
As in C, we can specify the initial index of a multidimensional array
to get an array-type object with fewer dimensions. In our example,
the variable chessBoard
is of type
ChessPiece[][]
. The expression
chessBoard[0]
is valid and refers to the first
element of chessBoard
, which is of type
ChessPiece[]
. For example, we can create a row for
our chess board:
ChessPiece [] startRow = { new ChessPiece("Rook"), new ChessPiece("Knight"), new ChessPiece("Bishop"), new ChessPiece("King"), new ChessPiece("Queen"), new ChessPiece("Bishop"), new ChessPiece("Knight"), new ChessPiece("Rook") }; chessBoard[0] = startRow;
We don’t necessarily have to
specify the dimension sizes of a multidimensional array with a single
new
operation. The syntax of the
new
operator lets us leave the sizes of some
dimensions unspecified. The size of at least the first dimension (the
most significant dimension of the array) has to be specified, but the
sizes of any number of the less significant array dimensions may be
left undefined. We can assign appropriate array-type values later.
We can create a checkerboard of boolean values (which is not quite sufficient for a real game of checkers) using this technique:
boolean [][] checkerBoard; checkerBoard = new boolean [8][];
Here, checkerBoard
is declared and created, but
its elements, the eight boolean[]
objects of the
next level, are left empty. Thus, for example,
checkerBoard[0]
is null
until
we explicitly create an array and assign it, as follows:
checkerBoard[0] = new boolean [8]; checkerBoard[1] = new boolean [8]; ... checkerBoard[7] = new boolean [8];
The code of the previous two examples is equivalent to:
boolean [][] checkerBoard = new boolean [8][8];
One reason we might want to leave dimensions of an array unspecified is so that we can store arrays given to us by another method.
Note that since the length of the array is not part of its type, the arrays in the checkerboard do not necessarily have to be of the same length. That is, multidimensional arrays do not have to be rectangular. Here’s a defective (but perfectly legal, to Java) checkerboard:
checkerBoard[2] = new boolean [3]; checkerBoard[3] = new boolean [10];
And here’s how you could create and initialize a triangular array:
int [][] triangle = new int [5][]; for (int i = 0; i < triangle.length; i++) { triangle[i] = new int [i + 1]; for (int j = 0; j < i + 1; j++) triangle[i][j] = i + j; }
We said earlier that arrays are instances of special array classes in the Java language. If arrays have classes, where do they fit into the class hierarchy and how are they related? These are good questions; however, we need to talk more about the object-oriented aspects of Java before answering them. That’s the subject of the next chapter. For now, take it on faith that arrays fit into the class hierarchy.
[16] The analog in C or C++ would be an array of pointers to objects. However, pointers in C or C++ are themselves two- or four-byte values. Allocating an array of pointers is, in actuality, allocating the storage for some number of those pointer objects. An array of references is conceptually similar, although references are not themselves objects. We can’t manipulate references or parts of references other than by assignment, and their storage requirements (or lack thereof ) are not part of the high-level Java language specification.
Get Learning Java now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.