The JVM has a set of instructions for allocating and using arrays. An array has two features: a type and a length. The type is fixed in the program, and the length is found on the operand stack. This arrangement makes it possible to type-check the program statically, but array bounds must be checked at run time.
4.12.1 Arrays of Reference
The anewarray instruction is used to create arrays of references. Like newarray (discussed in section 3.11), anewarray takes as an argument the type of elements you want in the array. To create an array of Strings five elements long:
iconst_5 anewarray java/lang/String
Note that the argument is the name of the class (java/lang/String) and not a type (Ljava/lang/String;).
This instruction creates a new object, which is an array of Strings. The type descriptor of the class of this object is [Ljava/lang/String;. The instruction leaves a reference to the new object on the operand stack.
The array object contains five slots, numbered from 0 to 4. All of them are initialized to null. The memory of the JVM looks like the diagram in Figure 4.8. To use this array, use aaload and aastore instructions. These instructions are just like ordinary load and store instructions, except that they require an additional operand: the number of the array element to retrieve. For example, to store Hello into slot 0 and World into slot 1, use these instructions (also see Figure 4.9):
dup ; Dup the array reference ldc "Hello" ; Store hello iconst_0 ; Into slot 0 aastore ; The array reference is still on the stack dup ldc "World" ; Store hello iconst_0 ; Into slot 0 aastore
Figure 4.8. New array of 5 Strings, all null
Figure 4.9. After setting array slots 0 and 1
To get elements out of the array, you use aaload. To get the reference to "World" on the stack, use
iconst_1 ; Push int 1 aaload ; Load array slot 1
Now the memory picture looks like the diagram in Figure 4.10 . The top of the stack has been replaced with a reference to the World string.
Figure 4.10. After loading array slot 1
You can think of an array as being a little like an object whose fields have numbers instead of names. Whenever you store into an array, you must meet the same requirement as if you were storing into the field: the object you are storing must have the same class as the array element type or some subclass of that class. Similarly, when you retrieve a value from that array, it will have the appropriate type.
4.12.2 Multidimensional Arrays
Both newarray and anewarray create one-dimensional arrays. If you want an array of arrays, a third array-creation instruction is needed: multianewarray. To the JVM a multidimensional array is really an array of arrays. The multianewarray instruction creates an object that is an array of arrays, and it may initialize some of those arrays.
Unlike newarray and anewarray, multianewarray takes a type name instead of a class name. It also takes a second argument: the number of dimensions to allocate (let's call it n). The JVM takes n int operands off the stack and uses them to determine how large an array to allocate.
To allocate a 3 x 5 array of ints, use
iconst_3 iconst_5 multianewarray [[I 2
This sequence of code allocates an array of three elements, each of which is a reference to an array of ints. Each of these subarrays is five elements long. In memory, it looks like the diagram in Figure 4.11.
Figure 4.11. An array int
There are no special instructions for getting values into and out of multidimensional arrays; you must handle each dimension individually. For example, to store the number 144 into the fifth column of the third row, you would say
; The object on top of the stack is [[I iconst_2 aaload ; Get the third element, which is ; an array of ints ([I) iconst_4 ; Store in the fifth element of ; the subarray ldc 144 ; Push the value to store iastore ; Store it in the fifth element
This code first loads the third element of the array. This is an array of ints. Then the code stores the value into the fifth element of that subarray. (All indexes are reduced by one in the code, because array indexes start at 0, so that the first element is element 0.)
It might seem that providing the number of dimensions to multianewarray is redundant, since it should be the same as the number of brackets at the beginning of the type. Actually, the number of dimensions may be less than the number of brackets, in which case you get an array of the same type with elements initialized to null instead of to another array. Allocating an array of the same type as before, but with fewer dimensions initialized, you get
iconst_3 multianewarray [[I 1
The result of executing this code is shown in Figure 4.12 . The object that was created is still an array-of-arrays-of-ints. However, only the first dimension has been initialized. You must always initialize at least one dimension of the array.
Figure 4.12. An array int
The only array-allocation instruction you really need is multianewarray since you can allocate a one-dimensional array of Strings of length 100 like this
bipush 100 multianewarray [Ljava/lang/String; 1
which has the same effect as
bipush 100 anewarray java/lang/String
4.12.3 Length of Arrays
In Java, to get the length of an array, you say
where args is a reference to an array object. Although the Java syntax for getting the length of an array looks as if you're getting the length field from the array object, that's not really what's happening. You must use the arraylength instruction on the array. To find out the length of the args array:
aload_0 ; Push the array reference arraylength ; Leaves the length of the array on the stack
This leaves the length of the array on the stack.