Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Java Tutor is my platform for teaching about Java 7+ and JavaFX 2.0+, mainly via programming projects.
java.lang.String objects containing command-line arguments that's passed to the public static void main(String[] args) entry-point method of the application's main class. Despite their familiarity, however, how much do you really know about Java's arrays?
This tutorial delves into the array feature. It first introduces this term and explores Java's one-dimensional and multidimensional array categories. Next, it reveals the exceptions that can be thrown while executing array-access code, presents class library support for arrays, and examines arrays in the context of additional language topics. Lastly, the tutorial examines arrays at the Java Virtual Machine (JVM) level.
Figure 1: A one-dimensional array offers a one-to-one correspondence between elements and indexes.
Figure 1 reveals that the first element has an index of 0, which is a common pattern that's followed by many languages including Java. It also reveals that indexes advance through consecutive positive integers.
You can create a one-dimensional array by employing the following syntax:
new type[size]
This syntax consists of keyword new followed by a type name (a primitive type reserved word such as int or the name of a reference type such as String), followed by a pair of square brackets that sandwich a 32-bit integer expression (denoting the number of array elements; its size), which evaluates to 0 or a positive integer. (This expression also can be of character type, but this is uncommon.)
Note: The maximum number of array elements can vary from JVM to JVM. For example, some JVMs may restrict this limit to Integer.MAX_VALUE-5, whereas JDK 7u6 on a 64-bit Windows platform restricts this limit to Integer.MAX_VALUE-2. The extra space is probably required for an array header. You will probably exhaust heap space and observe a thrown java.lang.OutOfMemoryError instance if you try to allocate this much space.
|
Here are a couple of examples:
new String[10] // create a 10-element array that can store String references new int['A'] // create a 65-element array that can store 32-bit integers; 65 is the Unicode value of A
When an array is created in this manner, each element is assigned a default zero value that's interpreted differently based on its type. For example, each element in the String array is interpreted as a null reference, each element in the int array is interpreted as 0, and each element in a Boolean array is interpreted as false.
You will probably want to initialize the array during creation, and you can do so by using an array initializer, which is a brace-delimited and comma-separated sequence of expressions that are type-compatible with the array type. The following syntax slightly modifies and extends the previous syntax to support an array initializer:
new type[] array initializer
Unlike in the previous syntax, you cannot specify a 32-bit integer/character expression between the square brackets. Fail to comply and the compiler reports an error. Also, an array initializer is specified after the closing square bracket. The following example uses this syntax to create and initialize a 5-element array of 32-bit integers:
new int[] { 10, 'A'+1, -32, 0, '\u0038' }
The compiler generates code to create a 5-element int array and assign initializer expression values to its elements (e.g., assign 10 to element 0).
The array initializer is syntactic sugar for making individual assignments to the various elements. Because this sugar is helpful, Java goes a bit further by letting you remove new int[]. Continuing from the previous example, you end up with { 10, 'A', -32, 0, '\u0038' } as your array specification. Behind the scenes, Java allocates memory for the array and performs the individual assignments.
Each of the previous examples returns a reference to the array. Although you can directly pass this reference to a method (e.g., sort(new int[] { 5, 3, 13, 1}); -- such an array is known as as an anonymous array and disappears after the method returns), you typically assign the reference to an array variable (to keep it around longer), which has the following syntax:
type[] identifier
This array variable declaration begins with a type, which is a primitive type reserved word or the name of a reference type, followed by a pair of square brackets (with nothing between them), followed by an identifier (which cannot be a reserved word). (Alternatively, the square brackets can appear after identifier, but cannot appear in both places.)
The following example declares array variable x, creates a 4-element one-dimensional array of floating-point values, and assigns the array to x:
float[] x = new float[4];
Figure 2 shows two ways in which this array variable might reference its array in memory.
Figure 2: Referencing an array directly via a memory address and indirectly via a handle table.
On some JVMs, the array variable could contain the actual memory address of the array, which is stored in a special region of memory known as the heap. On other JVMs, the array variable might contain an index into a special kind of array known as the handle table, and the indexed entry would contain the memory address. (Handle tables can simplify garbage collection.)
You can proceed to store values in and fetch values from the array by using the array index operator, whose syntax consists of array variable name[index], as follows:
x[0] = 23.5f; // Store 23.5 in first element -- the f suffix is necessary. System.out.println(x[0]); // Access first element -- output: 23.5
A one-dimensional array is associated with a length, which you can obtain by appending .length to the array variable name, as in int len = x.length; // assign array x's length to variable len. The .length expression refers to a special read-only int field (or property, if you prefer) that is associated with every one-dimensional array.
Figure 3: A two-dimensional array offers a one-to-one correspondence between elements and index pairs.
Figure 3 reveals that each element is associated with a pair of indexes, such as 2,1. The left index (2) refers to the row and the right index (1) refers to the column. The first element is located at 0,0.
You can create a two-dimensional array by employing the following syntax:
new type[rows][cols]
This syntax consists of keyword new followed by a type name (a primitive type reserved word such as int or the name of a reference type such as String), followed by a pair of square brackets that sandwich a 32-bit integer/character expression denoting the number of rows, followed by a pair of square brackets that sandwich a 32-bit integer/character expression denoting the number of columns.
Here is an example:
new boolean[10][30] // create a 10-row-by-30-column Boolean array; each element is initially false
You will probably want to initialize the table during creation, and you can do so by using a nested array initializer, which is a brace-delimited and comma-separated sequence of array initializers that are type-compatible with the array type. The following syntax slightly modifies and extends the previous syntax to support a nested array initializer:
new type[][] nested array initializer
Unlike in the previous syntax, you cannot specify a 32-bit integer/character expression between either pair of square brackets. Fail to comply and the compiler reports an error. Also, a nested array initializer is specified after the final closing square bracket. The following example uses this syntax to create and initialize a 3-row-by-2-column table of
String references (initially null):
new String[][] { { "A", "B" }, { "C", "D" }, { "E", "F" } }
The compiler generates code to create a 3-element String row array and a separate 2-element String column array for each row array element, and assign initializer expression values to column array elements (e.g., assign B to column array element 1 of row array element 0).
The nested array initializer is syntactic sugar for making individual assignments to the various elements. Because this sugar is helpful, Java goes a bit further by letting you remove new String[][]. Continuing from the previous example, you end up with { { "A", "B" }, { "C", "D" }, { "E", "F" } } as your array specification. (Java allocates memory
for the array and performs the individual assignments.)
Each of the previous examples returns a reference to the table. You can assign the reference to a table variable by specifying the following syntax:
type[][] identifier
This array variable declaration begins with a type, followed by a pair of square brackets (with nothing between them), followed by a second pair of empty square brackets, followed by an identifier (which cannot be a reserved word). (Alternatively, the square brackets can appear after identifier, but cannot appear in both places.)
| Caution: The number of square brackets associated with the array variable must match the number of square brackets associated with the array or agree with the amount of nesting in the nested array initializer. Otherwise, the compiler reports an error. |
The following example declares array variable x, creates a 4-row-by-4-column table of floating-point values, and assigns the table to x:
float[][] x = new float[4][4];
Figure 4 shows how this array variable and array are laid out in memory.
Figure 4: Referencing a row array from an array variable. (A handle table variation isn't shown for brevity.)
You can proceed to store values in and fetch values from the table by using the array index operator, whose syntax consists of array variable name[row][column], as follows:
x[0][1] = 23.5f; // Store 23.5 in element at (row 0, column 1). System.out.println(x[0][1]); // Access (row 0, column 1) element -- output: 23.5
You previously learned that a one-dimensional array is associated with a length. You can obtain the number of row elements in a table by appending .length to the array variable (e.g., x.length). You can obtain the number of column elements in a table by appending .length to the array index operator (e.g., x[0].length).
Expression x[0].length first accesses row 0 in table x, which returns a reference to the column array assigned to this row element. Because a column array is one-dimensional, you can append .length to this reference to return the number of elements in the column array. Because each column array has the same number of elements,
you can specify any row index (e.g., x[1].length).
Note: You may need to work with three-dimensional or higher-dimensional arrays. For a three-dimensional array, the array creation syntax is new type[tables][rows][cols], and you must specify three pairs of square brackets when declaring an array variable and when accessing the array. For array access, the leftmost index identifies the table that you want to access, the index to its right identifies the row index for that table, and the rightmost index identifies the column index for that table.
|
Figure 5: A square table that mirrors itself is a candidate for being turned into a ragged array.
When you look, for example, at (row 0, column 2) and (row 2, column 0), you'll notice the same value (2) in each location. This symmetry holds for the rest of the table. All values above and to the right of the diagonal from the upper-left corner to the lower-right corner are reflections of all values below and to the left of the diagonal. Therefore, we can throw away all values above and to the right, as follows:
int[][] x = new int[3][];
x[0] = new int[] { 1 };
x[1] = new int[] { 3, 1 };
x[2] = new int[] { 2, 4, 1 };
You first create the table by specifying the number of rows, but not the number of columns. For each row, you create a column array with the specified number of elements and assign its reference to the appropriate row array element. Although a little awkward to specify, this example reduces the total number of elements from 9 to 6. You'll observe greater memory savings when working with larger mirrored square tables.
You might wonder how to access any element in the ragged array (including nonexistent elements), but this isn't difficult. You swap the row and column indexes when the column index is greater than the row index; otherwise, you use the indexes as is. The following int get(int[][] table, int row, int column) method shows you how to access any element from the previous ragged array:
static int get(int[][] table, int row, int col)
{
if (col > row)
return table[col][row];
else
return table[row][col];
}
Continuing, the following code fragment shows you how to work with get() to print out all element values (including nonexistent element values):
for (int i = 0; i < x.length; i++)
{
for (int j = 0; j < x.length; j++)
System.out.print(get(x, i, j)+" ");
System.out.println();
}
Applying this code fragment to the previous ragged array results in the following output:
1 3 2 3 1 4 2 4 1
java.lang.ArrayIndexOutOfBoundsException class.
Attempting to store the wrong type of object in an array of objects is another problem that can occur in an array context. An attempt to store a superclass instance via a superclass array variable that is referencing an array of subclass type causes the JVM to create and throw an instance of the java.lang.ArrayStoreException class. Check out Listing 1.
class Superclass
{
int x;
}
class Subclass extends Superclass
{
int y = 1;
}
public class ArrayDemo
{
public static void main(String[] args)
{
Subclass[] subclassArray = new Subclass[1];
Superclass[] superclassArray = subclassArray;
superclassArray[0] = new Superclass();
// superclassArray[0] = new Subclass();
// System.out.println(((Subclass) superclassArray[0]).y);
}
}
Listing 1: ArrayDemo.java
Listing 1 first creates a single-element Subclass array and stores its reference in subclassArray. It next assigns subclassArray's reference to superclassArray, which is legal because of covariance (in which an array of supertype references is a supertype of an array of subtype references).
The third line compiles because superclassArray's type tells the compiler that its array can store Superclass instances. However, its execution results in ArrayStoreException because a
Superclass instance isn't a Subclass instance. Attempting to access the nonexistent y member (via ((Subclass) superclassArray[0]).y) would crash the JVM in the absence of this exception.
java.util.Arrays class, which offers static methods for sorting, searching, and otherwise manipulating arrays (and which is part of the Java Collections Framework). Most of its methods throw a java.lang.NullPointerException instance when passed a null array
reference. A few of the many methods are listed below:
<T> List<T> asList(T... a) returns a fixed-size list backed by the specified array. Any changes made to the list are reflected in the underlying array.
int binarySearch(int[] a, int key) employs the Binary Search algorithm to rapidly search through the specified integer array a for the specified key. The index (either 0 or a positive integer) is returned when the key is found. Otherwise, a negative index indicating that the key isn't present is returned.
boolean[] copyOf(boolean[] original, int newLength) copies the contents of the original Boolean array to a new Boolean array and returns a reference to this array. The new array has newLength elements. If the new array is smaller than original, it receives a truncated copy of original. If larger, additional entries are padded with false.
void fill(float[] a, float val) stores floating-point value val in each element of array a.
void sort(Object[] a) sorts a's elements according to their natural ordering. All elements must implement the java.lang.Comparable interface and be mutually comparable (i.e., e1.compareTo(e2) must not throw java.lang.ClassCastException for any array elements e1 and e2).
Listing 2 presents an ArrayDemo application that demonstrates void sort(Object[] a) and <T> void sort(T[] a, Comparator<? super T> c), which conveniently lets you change sort order by supplying an appropriate comparator (an object that implements the java.util.Comparator interface and imposes total ordering on a collection of objects).
import java.util.Arrays;
import java.util.Comparator;
public class ArrayDemo
{
public static void main(String[] args)
{
String[] planets = { "Jupiter", "Mars", "Neptune", "Venus", "Earth",
"Uranus", "Mercury", "Saturn" };
Arrays.sort(planets);
for (String planet: planets)
System.out.println(planet);
System.out.println();
Arrays.sort(planets, new Comparator()
{
@Override
public int compare(String o1, String o2)
{
return o2.compareTo(o1);
}
});
for (String planet: planets)
System.out.println(planet);
}
}
Listing 2: ArrayDemo.java
Listing 2 first creates a String array of planet names and then invokes void sort(Object[] a) to sort them in ascending order. These names are subsequently output. It then invokes <T> void sort(T[] a, Comparator<? super T> c) with a comparator that causes this array to be sorted in descending order. The resulting array is output.
Compile Listing 2 (javac ArrayDemo.java) and execute the resulting classfile (java ArrayDemo). You should observe the following output:
Earth Jupiter Mars Mercury Neptune Saturn Uranus Venus Venus Uranus Saturn Neptune Mercury Mars Jupiter Earth
While on the subject of the Collections Framework, the java.util.Collection interface declares a <T> T[] toArray(T[] a) generic method for returning an array containing the collection's elements. The returned array has the runtime type of the passed array. If this array has enough room, the collection is stored in the array. Otherwise, a new array is allocated, populated, and returned.
It's often convenient to pass a zero-length array and let this method allocate an array of the appropriate size. I demonstrate this feature in Listing 3.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArrayDemo
{
public static void main(String[] args)
{
List directions = new ArrayList<>();
directions.add("North");
directions.add("West");
directions.add("South");
directions.add("East");
for (String direction: directions)
System.out.println(direction);
System.out.println();
String[] dirs = directions.toArray(new String[0]);
Arrays.sort(dirs);
for (String dir: dirs)
System.out.println(dir);
}
}
Listing 3: ArrayDemo.java
Listing 3 first creates an array-based list and populates this collection with direction names, which are then output. Continuing, the collection is converted to an array via toArray() (note new String[0], which specifies the return type and tells toArray() to allocate this array), the resulting array is sorted, and the array is output.
Compile Listing 3 (javac ArrayDemo.java) and execute the resulting classfile (java ArrayDemo). You should observe the following output:
North West South East East North South West
java.lang.Class object. For example, String[].class returns the Class object for the String[] array type, and int[][].class returns the Class object for the int[][] array type. Once you have this object, you can invoke its boolean isArray() method to learn whether or not the object describes an array.
If you have a variable with an array type, you can determine if this variable is of a specific array type by invoking isArray() (e.g., ages.getClass().isArray(), where ages is of type int[]). Alternatively, you could use the instanceof operator (e.g., ages instanceof int[]).
Note: Stackoverflow's Java array reflection: isArray vs. instanceof topic points out that instanceof is the better choice from a performance perspective.
|
Finally, you may run into a strange situation when working with arrays in a generics context. For example, you have created a Stack<E> type, have introduced a private E[] elements; field declaration into this type, and have introduced a Stack(int size) constructor. If you specify elements = new E[size]; in the constructor, the compiler will complain.
The compiler outputs a generic array creation expression when it encounters the previous array-creation expression. It does so because such an expression could ultimately lead to a thrown ClassCastException instance, which is what generics attempt to avoid. The solution is to replace the previous expression with elements = (E[]) new Object[size];.
Listing 4 demonstrates these additional language topics.
public class ArrayDemo
{
public static void main(String[] args)
{
System.out.println(String[].class);
System.out.println(String[].class.isArray());
System.out.println(int[][].class);
System.out.println(int[][].class.isArray());
int[] ages = { 20, 89, 63, 42 };
if (ages instanceof int[])
System.out.println("ages is an int[] array");
else
System.out.println("ages isn't an int[] array");
if (ages.getClass().isArray())
System.out.println("ages is an int[] array");
else
System.out.println("ages isn't an int[] array");
}
}
class Stack
{
private E[] elements;
@SuppressWarnings("unchecked")
Stack(int size)
{
elements = (E[]) new Object[size];
// elements = new E[size];
}
}
Listing 4: ArrayDemo.java
Listing 4 first demonstrates that array types are associated with Class objects and then demonstrates the instanceof operator in an array context. Regarding the Stack<E> type,
@SuppressWarnings("unchecked") is used to suppress an unchecked cast warning on the (E[]) cast.
The warning message is the compiler's way of noting the possibility that downcasting from Object[] to E[] could violate type safety, because any type of object (e.g., a String object) could be stored in the Object[] array. However, this cannot happen in this example, and so it's okay to suppress this warning.
Compile Listing 4 (javac ArrayDemo.java) and execute the resulting classfile (java ArrayDemo). You should observe the following output:
class [Ljava.lang.String; true class [[I true ages is an int[] array ages is an int[] array
newarray: Create a new one-dimensional array that stores primitive type values -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.newarray for more information.
anewarray: Create a new one-dimensional array that stores reference values -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.anewarray for more information.
multianewarray: Create a new multidimensional array that stores reference values -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.multianewarray for more information.
At some point, you'll want to store values in array elements. The JVM provides the following array-store instructions:
bastore: Store byte or Boolean value into byte or boolean array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.bastore for more information.
castore: Store character value into char array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.castore for more information.
sastore: Store short integer value into short array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.sastore for more information.
iastore: Store integer value into int array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iastore for more information.
lastore: Store long integer value into long array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.lastore for more information.
fastore: Store floating-point value into float array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.fastore for more information.
dastore: Store double precision floating-point value into double array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.dastore for more information.
aastore: Store reference value into reference array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.aastore for more information.
To complement the previous array-store instructions, the JVM provides the following instructions for reading the contents of array elements:
baload: Load byte or Boolean value from byte or boolean array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.baload for more information.
caload: Load character value from char array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.caload for more information.
saload: Load short integer value from short array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.saload for more information.
iaload: Load integer value from int array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.iaload for more information.
laload: Load long integer value from long array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.laload for more information.
faload: Load floating-point value from float array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.faload for more information.
daload: Load double precision floating-point value from double array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.daload for more information.
aaload: Load reference value from reference array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.aaload for more information.
Finally, the JVM provides one miscellanous array-oriented instruction, which is described below:
arraylength: Return the length of an array -- see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.arraylength for more information.
Listing 5 presents a small application that will be used to demonstrate some of these JVM instructions.
public class ArrayDemo
{
public static void main(String[] args)
{
int[] grades = { 85, 94, 72 };
String[] cities = { "London", "Paris" };
}
}
Listing 5: ArrayDemo.java
Listing 6 presents the JVM equivalent of Listing 5's main() method. The zero-based offset of the JVM instruction appears on the left of the JVM instruction mnemonic.
0 iconst_3 1 newarray int 3 dup 4 iconst_0 5 bipush 85 7 iastore 8 dup 9 iconst_1 10 bipush 94 12 iastore 13 dup 14 iconst_2 15 bipush 72 17 iastore 18 astore_1 19 iconst_2 20 anewarray java/lang/String 23 dup 24 iconst_0 25 ldc "London" 27 aastore 28 dup 29 iconst_1 30 ldc "Paris" 32 aastore 33 astore_2 34 return
Listing 6: The JVM equivalent of Listing 5
This code sequence can be interpreted as follows:
iconst_3 pushes integer constant 3 onto the operand stack, which is a data structure for storing constants, local variable values, and field values on which various JVM instructions operate. This integer identifies the length of the grades array. (The operand stack is part of a larger structure called a frame, which gets
created when a method is invoked, and which stores the operand stack. To learn more, check out Section 2.6 "Frames" in the JVM documentation.)
newarray int pops 3 from the operand stack and creates an array of three 32-bit integers -- the int operand (actually, a numeric code operand whose value is 10 for int) identifies the type of the elements in this one-dimensional array. A reference to this array, where each element initially stores 0, is pushed onto the operand stack.
dup pushes a duplicate of the value at the top of the operand stack (the array reference) onto the operand stack.
iconst_0 pushes integer constant 0 onto the operand stack. This value is the index of the first entry in the array.
bipush 85 sign-extends byte integer 85 into an int and pushes the result onto the operand stack.
iastore pops the duplicate array reference, index 0, and integer value 85 from the operand stack, and stores 85 at index 0 in this array.
dup pushes a duplicate of the value at the top of the operand stack (the array reference) onto the operand stack.
iconst_1 pushes integer constant 1 onto the operand stack. This value is the index of the second entry in the array.
bipush 94 sign-extends byte integer 94 into an int and pushes the result onto the operand stack.
iastore pops the duplicate array reference, index 1, and integer value 94 from the operand stack, and stores 94 at index 1 in this array.
dup pushes a duplicate of the value at the top of the operand stack (the array reference) onto the operand stack.
iconst_2 pushes integer constant 2 onto the operand stack. This value is the index of the third entry in the array.
bipush 72 sign-extends byte integer 72 into an int and pushes the result onto the operand stack.
iastore pops the duplicate array reference, index 2, and integer value 72 from the operand stack, and stores 72 at index 2 in this array.
astore_1 pops the array reference from the operand stack and stores it in local variable 1, which corresponds to grades in Listing 5. The operand stack is empty at this point.
iconst_2 pushes integer constant 2 onto the operand stack. This integer identifies the length of the cities array.
anewarray java/lang/String pops 2 from the operand stack and creates an array of two java/lang/String references. A reference to this array, where each element initially stores a null reference, is
pushed onto the operand stack.
dup pushes a duplicate of the value at the top of the operand stack (the array reference) onto the operand stack.
iconst_0 pushes integer constant 0 onto the operand stack. This value is the index of the first entry in the array.
ldc "London" pushes a reference to the interned "London" string (which is initially stored in the constant pool, a classfile data structure that stores various constants) onto the operand stack.
aastore pops the duplicate array reference, index 0, and the string reference from the operand stack, and stores this reference at index 0 in this array.
dup pushes a duplicate of the value at the top of the operand stack (the array reference) onto the operand stack.
iconst_1 pushes integer constant 1 onto the operand stack. This value is the index of the second entry in the array.
ldc "Paris" pushes a reference to the interned "Paris" string (which is initially stored in the constant pool) onto the operand stack.
aastore pops the duplicate array reference, index 1, and the string reference from the operand stack, and stores this reference at index 1 in this array.
astore_2 pops the array reference from the operand stack and stores it in local variable 2, which corresponds to cities in Listing 5. The operand stack is empty at this point.
return returns control from the main() method to its invoker, which happens to be whatever code invoked the main() method.
Listing 7 presents a small application that will be used to demonstrate additional JVM instructions.
public class ArrayDemo
{
public static void main(String[] args)
{
double[][] temps = new double[2][1];
temps[0][0] = 23.5;
temps[1][0] = 86.2;
for (int i = 0; i < temps.length; i++)
{
for (int j = 0; j < temps[0].length; j++)
System.out.print(temps[i][j]);
System.out.println();
}
}
}
Listing 7: ArrayDemo.java
Listing 8 presents the JVM equivalent of Listing 7's main() method. The zero-based offset of the JVM instruction appears on the left of the JVM instruction mnemonic.
0 iconst_2
1 iconst_1
2 multianewarray [[D 2
6 astore_1
7 aload_1
8 iconst_0
9 aaload
10 iconst_0
11 ldc2_w #23.5
14 dastore
15 aload_1
16 iconst_1
17 aaload
18 iconst_0
19 ldc2_w #86.2
22 dastore
23 iconst_0
24 istore_2
25 iload_2
26 aload_1
27 arraylength
28 if_icmpge 70
31 iconst_0
32 istore_3
33 iload_3
34 aload_1
35 iconst_0
36 aaload
37 arraylength
38 if_icmpge 58
41 getstatic java/lang/System/out Ljava/io/PrintStream;
44 aload_1
45 iload_2
46 aaload
47 iload_3
48 daload
49 invokevirtual java/io/PrintStream/print(D)V
52 iinc 3 1
55 goto 33
58 getstatic java/lang/System/out Ljava/io/PrintStream;
61 invokevirtual java/io/PrintStream/println()V
64 iinc 2 1
67 goto 25
70 return
Listing 8: The JVM equivalent of Listing 7
This code sequence can be interpreted as follows:
iconst_2 pushes integer constant 2 onto the operand stack. This value is the number of rows for the two-dimensional array that is about to be created.
iconst_1 pushes integer constant 1 onto the operand stack. This value is the number of columns for the two-dimensional array that is about to be created.
multianewarray [[D 2 creates a two-dimensional array. Its [[D operand (actually, an index into the constant pool whose entry contains [[D) is a symbolic reference to the double[][] type, and its 2 operand identifies the number of dimensions. This instruction uses the number of dimensions to determine how
many operands to pop off the operand stack. It does so, creates the two-dimensional array, and pushes its reference (which is actually a reference to the row array) onto the operand stack.
astore_1 pops the row array reference from the operand stack and stores it in local variable 1, which corresponds to temps (temperatures) in Listing 7.
aload_1 pushes the row array reference in local variable 1 onto the operand stack.
iconst_0 pushes integer constant 0 onto the operand stack. This value identifies row 0 in the two-dimensional array.
aaload pops the row array reference and row index from the operand stack, retrieves the column array reference at index 0, and pushes this column array reference onto the operand stack.
iconst_0 pushes integer constant 0 onto the operand stack. This value is the column index in the two-dimensional array.
ldc2_w #23.5 pushes double precision floating-point constant 23.5 from the constant pool onto the operand stack.
dastore pops the column array reference, index 0, and 23.5 from the operand stack and stores this value at index 0 in this array.
aload_1 pushes the row array reference in local variable 1 onto the operand stack.
iconst_1 pushes integer constant 1 onto the operand stack. This value identifies row 1 in the two-dimensional
array.
aaload pops the row array reference and row index from the operand stack, retrieves the column array reference at index 1, and pushes this column array reference onto the operand stack.
iconst_0 pushes integer constant 0 onto the operand stack. This value is the column index in the two-dimensional array.
ldc2_w #86.2 pushes double precision floating-point constant 86.2 from the constant pool onto the operand stack.
dastore pops the column array reference, index 0, and 86.2 from the operand stack and stores this value at index 0 in this array.
iconst_0 pushes integer constant 0 onto the operand stack. This value is the initial value for local variable i.
istore_2 pops integer constant 0 from the operand stack and initializes local variable 2, which corresponds to i in Listing 7.
iload_2 pushes the contents of int-based local variable 2 (0) onto the operand stack.
aload_1 pushes the row array reference in local variable 1 onto the operand stack.
arraylength pops the row array reference from the operand stack, obtains its length (2), and pushes the length onto the operand stack.
if_icmpge 70 pops the previously pushed contents of local variable 2 (i) and the row array length from the operand stack and compares them. If local variable 2 contains a value that is greater than or equal to the array length, execution is transferred to the return instruction at offset 70 (and main() exits).
Otherwise, execution falls through to the next instruction.
iconst_0 pushes integer constant 0 onto the operand stack. This value is the column index for the first column in the column array.
istore_3 pops integer constant 0 from the operand stack and initializes local variable 3, which corresponds to j in Listing 7.
iload_3 pushes the contents of int-based local variable 3 (0) onto the operand stack.
aload_1 pushes the row array reference in local variable 1 onto the operand stack.
iconst_0 pushes integer constant 0 onto the operand stack. This value is the row index for the first row in the row array.
aaload pops the row array reference and row index from the operand stack, retrieves the column array reference at index 0, and pushes this column array reference onto the operand stack.
arraylength pops the column array reference from the operand stack, obtains its length (1), and pushes the length onto the operand stack.
if_icmpge 58 pops the previously pushed contents of local variable 3 (j) and the column array length from the operand stack and compares them. If local variable 3 contains a value that is greater than or equal to the array length, execution is transferred to the getstatic java/lang/System/out Ljava/io/PrintStream; instruction
at offset 58. Otherwise, execution falls through to the next instruction.
getstatic java/lang/System/out Ljava/io/PrintStream; pushes a reference to the System.out static field onto the operand stack.
aload_1 pushes the row array reference in local variable 1 onto the operand stack.
iload_2 pushes the contents of int-based local variable 2 (i) onto the operand stack.
aaload pops the row array reference and row index from the operand stack, retrieves the column array reference at index i, and pushes this column array reference onto the operand stack.
iload_3 pushes the contents of int-based local variable 3 (j) onto the operand stack.
daload pops the column array reference and column index from the operand stack, retrieves the double value at index j, and pushes this value onto the operand stack.
invokevirtual java/io/PrintStream/print(D)V pops the System.out reference from the operand stack and invokes System.out.print(double), which pops the double value from the operand stack and outputs this value.
iinc 3 1 increments local variable 3 (j) by 1.
goto 33 transfers control to the beginning of the test portion of the for j loop.
getstatic java/lang/System/out Ljava/io/PrintStream; pushes a reference to the System.out static field onto the operand stack.
invokevirtual java/io/PrintStream/println()V pops the System.out reference from the operand stack and invokes System.out.println(), which outputs a platform-dependent newline character sequence in a platform-independent manner.
iinc 2 1 increments local variable 2 (i) by 1.
goto 25 transfers control to the beginning of the test portion of the for i loop.
return returns control from the main() method to its invoker, which happens to be whatever code invoked the main() method.
new int['0']?
elements = new E[size]; ultimately lead to a thrown ClassCastException instance (if allowed)?
You can download this post's code and answers here. Code was developed and tested with JDK 7u6 on a Windows 7 platform.
* * *
I welcome your input to this blog, and will write about relevant topics that you suggest. While waiting for the next blog post, check out my TutorTutor website to learn more about Java and other computer technologies (and that's just the beginning).
|
Learn more about the Java 7 language and many of its APIs by reading my book Beginning Java 7. You can obtain information about this book here and here. |