Array initialization
An array is simply a sequence of either objects or primitives that are all the same type and are packaged together under one identifier name. Arrays are defined and used with the square-brackets indexing operator [ ]. To define an array reference, you simply follow your type name with empty square brackets:
int[ ] a1;
You can also put the square brackets after the identifier to produce exactly the same meaning:
int a1[ ];
This conforms to expectations from C and C++ programmers. The former style, however, is probably a more sensible syntax, since it says that the type is “an int array.” That style will be used in this book.
The compiler doesn’t allow you to tell it how big the array is. This brings us back to that issue of “references.” All that you have at this point is a reference to an array (you’ve allocated enough storage for that reference), and there’s been no space allocated for the array object itself. To create storage for the array, you must write an initialization expression. For arrays, initialization can appear anywhere in your code, but you can also use a special kind of initialization expression that must occur at the point where the array is created. This special initialization is a set of values surrounded by curly braces. The storage allocation (the equivalent of using new) is taken care of by the compiler in this case. For example:
int[] a1 = { 1, 2, 3, 4, 5 };
So why would you ever define an array reference without an array?
int[] a2;
Well, it’s possible to assign one array to another in Java, so you can say:
a2 = a1;
What you’re really doing is copying a reference, as demonstrated here:
//: initialization/ArraysOfPrimitives.java import static net.mindview.util.Print.*; public class ArraysOfPrimitives { public static void main(String[] args) { int[] a1 = { 1, 2, 3, 4, 5 }; int[] a2; a2 = a1; for(int i = 0; i < a2.length; i++) a2[i] = a2[i] + 1; for(int i = 0; i < a1.length; i++) print("a1[" + i + "] = " + a1[i]); } } /* Output: a1[0] = 2 a1[1] = 3 a1[2] = 4 a1[3] = 5 a1[4] = 6 *///:~
You can see that a1 is given an initialization value but a2 is not; a2 is assigned later—in this case, to another array. Since a2 and a1 are then aliased to the same array, the changes made via a2 are seen in a1.
All arrays have an intrinsic member (whether they’re arrays of objects or arrays of primitives) that you can query—but not change—to tell you how many elements there are in the array. This member is length. Since arrays in Java, like C and C++, start counting from element zero, the largest element you can index is length - 1. If you go out of bounds, C and C++ quietly accept this and allow you to stomp all over your memory, which is the source of many infamous bugs. However, Java protects you against such problems by causing a runtime error (an exception) if you step out of bounds.5
What if you don’t know how many elements you’re going to need in your array while you’re writing the program? You simply use new to create the elements in the array. Here, new works even though it’s creating an array of primitives (new won’t create a non-array primitive):
// Creating arrays with new. import java.util.*; import static net.mindview.util.Print.*; public class ArrayNew { public static void main(String[] args) { int[] a; Random rand = new Random(47); a = new int[rand.nextInt(20)]; print("length of a = " + a.length); print(Arrays.toString(a)); } } /* Output: length of a = 18 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] *///:~
The size of the array is chosen at random by using the Random.nextInt( ) method, which produces a value between zero and that of its argument. Because of the randomness, it’s clear that array creation is actually happening at run time. In addition, the output of this program shows that array elements of primitive types are automatically initialized to “empty” values. (For numerics and char, this is zero, and for boolean, it’s false.)
The Arrays.toString( ) method, which is part of the standard java.util library, produces a printable version of a one-dimensional array.
Of course, in this case the array could also have been defined and initialized in the same statement:
int[] a = new int[rand.nextInt(20)];
This is the preferred way to do it, if you can.
If you create a non-primitive array, you create an array of references. Consider the wrapper type Integer, which is a class and not a primitive:
//: initialization/ArrayClassObj.java // Creating an array of nonprimitive objects. import java.util.*; import static net.mindview.util.Print.*; public class ArrayClassObj { public static void main(String[] args) { Random rand = new Random(47); Integer[] a = new Integer[rand.nextInt(20)]; print("length of a = " + a.length); for(int i = 0; i < a.length; i++) a[i] = rand.nextInt(500); // Autoboxing print(Arrays.toString(a)); } } /* Output: (Sample) length of a = 18 [55, 193, 361, 461, 429, 368, 200, 22, 207, 288, 128, 51, 89, 309, 278, 498, 361, 20] *///:~
Here, even after new is called to create the array:
Integer[] a = new Integer[rand.nextInt(20)];
it’s only an array of references, and the initialization is not complete until the reference itself is initialized by creating a new Integer object (via autoboxing, in this case):
a[i] = rand.nextInt(500);
If you forget to create the object, however, you’ll get an exception at run time when you try to use the empty array location.
It’s also possible to initialize arrays of objects by using the curly brace-enclosed list. There are two forms:
//: initialization/ArrayInit.java // Array initialization. import java.util.*; public class ArrayInit { public static void main(String[] args) { Integer[] a = { new Integer(1), new Integer(2), 3, // Autoboxing }; Integer[] b = new Integer[]{ new Integer(1), new Integer(2), 3, // Autoboxing }; System.out.println(Arrays.toString(a)); System.out.println(Arrays.toString(b)); } } /* Output: [1, 2, 3] [1, 2, 3] *///:~
In both cases, the final comma in the list of initializers is optional. (This feature makes for easier maintenance of long lists.)
Although the first form is useful, it’s more limited because it can only be used at the point where the array is defined. You can use the second and third forms anywhere, even inside a method call. For example, you could create an array of String objects to pass to the main( ) of another method, to provide alternate command-line arguments to that main( ):
//: initialization/DynamicArray.java // Array initialization. public class DynamicArray { public static void main(String[] args) { Other.main(new String[]{ "fiddle", "de", "dum" }); } } class Other { public static void main(String[] args) { for(String s : args) System.out.print(s + " "); } } /* Output: fiddle de dum *///:~
The array created for the argument of Other.main( ) is created at the point of the method call, so you can even provide alternate arguments at the time of the call.
[Thinking in Java, 133~]