6.6 Arrays: Creation, Multidimensional Forms, and the Arrays Utility Class
Key Takeaways
- Bracket placement changes meaning in multi-variable declarations: int[] x, y; declares two int arrays, while int p[], q; declares one int array and one plain int.
- Array elements are default-initialized on allocation to 0, 0.0, the null character, false, or null; length is a final field rather than a method, and a negative size throws NegativeArraySizeException at run time.
- Arrays are covariant, so assigning a String[] to an Object[] compiles but storing an incompatible element throws ArrayStoreException; generics are invariant and catch the same mistake at compile time.
- Arrays.equals and Arrays.toString inspect only one level, so nested arrays require Arrays.deepEquals and Arrays.deepToString.
- Arrays.asList returns a fixed-size view whose add and remove throw UnsupportedOperationException, and passing an int[] yields a one-element list rather than a list of three Integers.
Arrays: Creation, Multidimensional Forms, and the java.util.Arrays Utility Class
Oracle's Working with Arrays and Collections objective opens with the words "Create arrays, List, Set, Map and Deque collections, and add, remove, update, retrieve and sort their elements." Arrays are named first for a reason: they are the only container built into the language itself, they behave differently from every collection you have met so far, and 1Z0-830 uses them constantly as the vehicle for questions about declaration syntax, default values, covariance, and the Arrays helper methods.
1. Declaration Syntax and the Comma Trap
The square brackets may sit after the type or after the variable name. Both compile; only one is idiomatic.
int[] a; // preferred
int b[]; // legal, C-style
int[] c[]; // legal - this is int[][]
The position matters enormously once you declare several variables in one statement:
int[] x, y; // BOTH x and y are int[]
int p[], q; // p is int[]; q is a plain int!
int[] r, s[]; // r is int[]; s is int[][]
Brackets are not part of the type in var declarations, and an array initializer alone gives the compiler nothing to infer from:
var t = new int[]{1, 2, 3}; // OK -> int[]
// var u = {1, 2, 3}; // ERROR: array initializer needs an explicit target type
2. Creation and Default Element Values
There are three ways to build an array, and the brace-only shorthand works only in a declaration:
int[] sized = new int[5]; // length 5, every element 0
int[] literal = new int[]{10, 20, 30}; // anonymous array - usable anywhere
int[] shortcut = {10, 20, 30}; // ONLY valid in a declaration statement
int[] later;
// later = {1, 2, 3}; // ERROR
later = new int[]{1, 2, 3}; // OK
Every element is initialized to the type's default the moment the array is allocated - arrays never contain "undefined" slots the way uninitialized local variables do:
| Element type | Default value |
|---|---|
byte, short, int, long | 0 |
float, double | 0.0 |
char | the null character, escape \\u0000 (prints as a blank) |
boolean | false |
| Any reference type | null |
length is a final field, not a method - the classic three-way confusion the exam loves:
int[] nums = new int[3];
nums.length; // FIELD access, no parentheses
"Duke".length(); // String METHOD
List.of(1).size(); // Collection METHOD
Two runtime failures to recognize on sight:
int size = -1;
int[] bad = new int[size]; // compiles; throws NegativeArraySizeException
nums[3] = 9; // compiles; throws ArrayIndexOutOfBoundsException
3. Multidimensional and Jagged Arrays
Java has no true rectangular arrays - a two-dimensional array is an array whose elements are themselves array references.
int[][] grid = new int[3][4]; // fully allocated 3 x 4, all zeros
int[][] rows = new int[3][]; // 3 slots, each currently null
rows[0] = new int[2];
rows[1] = new int[5]; // rows may differ in length: a "jagged" array
// rows[2] stays null -> touching rows[2][0] throws NullPointerException
int[][] fromLiteral = {{1, 2}, {3, 4, 5}, {}};
Only the leftmost dimensions may be sized: new int[3][] is fine, but new int[][3] does not compile.
4. Covariance and ArrayStoreException
Arrays are covariant: String[] is a subtype of Object[]. Generics are invariant, which is why List<String> is not a List<Object> (section 6.2). Covariance means the compiler accepts assignments it cannot prove safe, so the check is deferred to run time.
Object[] objects = new String[2]; // compiles - covariance
objects[0] = "fine"; // OK
objects[1] = Integer.valueOf(42); // compiles, but throws ArrayStoreException at run time
Arrays also inherit equals, hashCode, and toString straight from Object without overriding them:
int[] m = {1, 2};
int[] n = {1, 2};
System.out.println(m.equals(n)); // false - reference identity
System.out.println(m); // e.g. [I@6d06d69c
System.out.println(Arrays.toString(m)); // [1, 2]
clone() on an array is a shallow copy: cloning an int[][] duplicates the outer array but shares the row objects.
5. The java.util.Arrays Toolbox
| Method | Behaviour worth memorizing |
|---|---|
Arrays.toString(a) | One-dimensional rendering; on a nested array it prints row references |
Arrays.deepToString(a) | Recursive rendering for nested arrays |
Arrays.equals(a, b) | Element-wise, but only one level deep |
Arrays.deepEquals(a, b) | Recursive comparison for nested arrays |
Arrays.sort(a) | Ascending natural order; the Comparator overload exists for object arrays only |
Arrays.sort(a, from, to) | Sorts the half-open range [from, to) |
Arrays.binarySearch(a, key) | Requires a sorted array; on unsorted input the result is undefined, not an exception |
Arrays.fill(a, v) | Overwrites every element (or a range) with v |
Arrays.copyOf(a, n) | Truncates, or pads with the element type's default |
Arrays.copyOfRange(a, from, to) | Half-open range copy |
Arrays.asList(...) | Fixed-size view backed by the array |
Arrays.stream(a) | IntStream/LongStream/DoubleStream for primitives, Stream<T> for objects |
Arrays.compare / Arrays.mismatch | Lexicographic comparison, and the first differing index (-1 when equal) |
When the key is absent, binarySearch returns -(insertion point) - 1, exactly as Collections.binarySearch does (section 6.5).
Arrays.asList - Two Separate Traps
List<String> view = Arrays.asList("a", "b", "c");
view.set(0, "z"); // OK - writes through to the backing array
// view.add("d"); // UnsupportedOperationException - fixed size
// view.remove(0); // UnsupportedOperationException
Autoboxing does not reach inside an array, so a primitive array becomes a single element:
int[] primitives = {1, 2, 3};
System.out.println(Arrays.asList(primitives).size()); // 1 -> List<int[]>
Integer[] boxed = {1, 2, 3};
System.out.println(Arrays.asList(boxed).size()); // 3 -> List<Integer>
// Correct primitive conversion:
List<Integer> proper = Arrays.stream(primitives).boxed().toList(); // size 3
Copying Between Arrays
System.arraycopy(src, srcPos, dest, destPos, length) is the low-level primitive that copyOf and ArrayList growth are built on; going the other way, list.toArray(new String[0]) produces a correctly typed array.
6. Arrays Versus Collections
| Aspect | Array | List |
|---|---|---|
| Size | Fixed at creation | Grows and shrinks |
| Holds primitives | Yes | No - elements are boxed |
| Size query | length field | size() method |
| Type relationship | Covariant (ArrayStoreException) | Invariant (compile-time safety) |
| Sequenced API (Java 21) | Not applicable | getFirst(), reversed(), and friends |
Reach for an array when the length is genuinely fixed and the elements are primitives; reach for a collection in almost every other case.
Given the declaration below, what are the types of p and q?
int p[], q;
What is the result of executing the following code?
Object[] values = new String[2];
values[0] = "Duke";
values[1] = Integer.valueOf(42);
System.out.println(values.length);
What does the following code print?
int[] primitives = {1, 2, 3};
Integer[] boxed = {1, 2, 3};
System.out.println(Arrays.asList(primitives).size() + " " + Arrays.asList(boxed).size());
Given the nested arrays below, what is printed?
int[][] left = {{1, 2}, {3, 4}};
int[][] right = {{1, 2}, {3, 4}};
System.out.println(Arrays.equals(left, right) + " " + Arrays.deepEquals(left, right));