Arrays
Arrays are fixed-length, indexed sequences of a single element type. They're a built-in language feature, separate from the dynamic ArrayList<T> in the standard library — when you need a growable sequence, reach for ArrayList<T> instead.
Declaration
Two ways to create an array:
// 1. Allocate a fixed-size array; elements default to zero / null.
int[] nums = new int[10];
string[] names = new string[3];
// 2. Initialize from a literal.
int[] primes = [2, 3, 5, 7, 11];
string[] words = ["hello", "world"];
The element type sits before the []. Any type works as an element: primitives, classes, interfaces, generics, lambdas (via SAM-interface types).
Indexing
Zero-based, with bounds checking:
int[] a = [10, 20, 30];
print(a[0]); // 10
a[2] = 99;
print(a[2]); // 99
Out-of-range access throws IndexOutOfBoundsException.
length
Every array exposes a length property:
int[] a = [1, 2, 3, 4, 5];
for (int i = 0; i < a.length; i++) {
print(a[i]);
}
Iteration
int[] a = [1, 2, 3, 4, 5];
// Indexed
for (int i = 0; i < a.length; i++) {
print(a[i]);
}
// Enhanced for (for-each)
for (int x : a) {
print(x);
}
The for-each form works on any array regardless of element type.
Multi-Dimensional Arrays
Multi-dim arrays are arrays-of-arrays:
int[][] matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]];
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
print(matrix[i][j]);
}
}
You can also allocate them shape-first:
int[][] grid = new int[3][];
for (int i = 0; i < grid.length; i++) {
grid[i] = new int[3];
}
Sub-arrays in K[][] are views, not standalone arrays you can swap. Assigning a whole row (grid[i] = newRow) raises a runtime error in current builds — replace elements individually.
Arrays of Class / Interface Types
Class- and interface-typed arrays are first-class:
interface Drawable {
function draw(): string;
}
Drawable[] shapes = new Drawable[3];
shapes[0] = new Circle(5);
shapes[1] = new Rectangle(10, 20);
shapes[2] = new Circle(3);
for (int i = 0; i < shapes.length; i++) {
print(shapes[i].draw());
}
A subclass instance can be stored in a base-class slot — Animal[] a = new Animal[3]; a[0] = new Dog(...); works because each element assignment is an ordinary upcast.
Array Reference Invariance
While element assignment respects subtyping, whole-array references are invariant:
Dog[] dogs = new Dog[2];
Animal[] animals = dogs; // ✗ rejected — array references are invariant
This is intentional — it prevents writes through an Animal[] alias from depositing a non-Dog into the underlying Dog[]. To pass elements into a base-typed slot, copy them individually:
Animal[] animals = new Animal[dogs.length];
for (int i = 0; i < dogs.length; i++) {
animals[i] = dogs[i]; // ✓ element-wise upcast
}
Arrays as Parameters and Return Values
Arrays can be passed and returned like any other reference type:
function sum(int[] xs): int {
int total = 0;
for (int x : xs) { total = total + x; }
return total;
}
function range(int n): int[] {
int[] out = new int[n];
for (int i = 0; i < n; i++) { out[i] = i; }
return out;
}
Mutations made by the callee are visible to the caller — array values are passed by reference, not copied.
When to Use What
| Need | Use |
|---|---|
| Fixed-size, low-overhead, primitive-heavy | Built-in array (int[], float[]) |
Growable, with add / remove / forEach | ArrayList<T> |
| Streaming / functional pipelines | Stream API on top of either |
See Also
- Standard Library / Collections —
ArrayList<T>,HashMap<K,V>, etc. - Generics
- Control Flow — for/for-each loops