Topic 3.8
Arrays
In one line
An array is a fixed-size, numbered row of values of one type, stored together as a single object on the heap. You reach each value by its index, from 0 to length - 1, in constant time.
Think of it like this
A row of numbered post-office boxes. There are exactly 10 boxes, numbered 0 to 9, all the same size, side by side. To get a letter you don't search; you go straight to box 7. You can change what's inside a box, but you can't add an eleventh box to the row. For more boxes you'd need a new, bigger row and to move every letter across.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Array
- A fixed-size, numbered sequence of values of one type, stored as one object.
- Element
- One value stored in an array.
- Index
- The position number of an element. The first is 0, the last is
length - 1. - Length
- How many elements an array has, read with
arr.length. It's fixed when the array is created. - Default value
- The value an element has before you assign one:
0,0.0,false, the zerochar, ornull. - Array initializer
- Braces with values,
{2, 3, 5}, that create an array with those elements and the matching length. - Alias
- A second variable referring to the same array. Changes through one are visible through the other.
- Out of bounds
- An index below 0 or at least
length. Using one throwsArrayIndexOutOfBoundsException. - Shallow copy
- A copy of the array itself. For arrays of objects it copies the references, not the objects they point to.
Step by step
01Why arrays: one name for many values
To store five test scores you could write score1, score2... score5. But then you can't loop over them, and 500 scores would need 500 variables.
An array gives one name to the whole group and a number to each slot, so a loop can visit every element: for (int i = 0; i < scores.length; i++).
02Declaring and creating
There are two steps, often written on one line. Declare a variable of an array type: int[] scores; (the brackets belong to the type). Create the array object: scores = new int[5];.
Or use an initializer when you know the values: String[] days = {"Mon", "Tue", "Wed"};. The longer form new String[] {"Mon", "Tue"} works anywhere, including as a method argument or return value.
Java also accepts the C-style int scores[], but int[] scores is the standard style: it keeps the whole type in one place.
int[] scores = new int[5]; // [0, 0, 0, 0, 0]
String[] names = new String[3]; // [null, null, null]
int[] primes = {2, 3, 5, 7, 11}; // length 5
double[] prices = new double[] {9.5, 12.0}; // works in any expression
int[] empty = new int[0]; // legal: length 003Indexes and length
Indexes start at 0 because an index is really an offset from the start of the array: element i is i steps from the first one. That's how the JVM finds any element in one step: start address + i times the element size.
length is a field, not a method: scores.length, never scores.length() (that's for String). The last valid index is always length - 1, so loops use i < arr.length, not <=.
04Every access is checked
In C, reading past the end of an array silently reads whatever memory is next, a huge source of crashes and security holes. Java checks every index at run time and throws ArrayIndexOutOfBoundsException with the bad index and the length.
The check is cheap, and the JIT removes it when it can prove a loop's index stays in range (called range check elimination), which is one more reason to write the standard for (int i = 0; i < arr.length; i++) loop.
Creating an array with a negative length fails too: new int[-1] throws NegativeArraySizeException: -1.
05Looping over arrays
Use the index loop when you need the position or want to change elements: for (int i = 0; i < arr.length; i++) arr[i] *= 2;.
Use the enhanced for loop (Topic 2.7) when you only read values: for (int s : scores) sum += s;. Assigning to s inside it changes only the loop variable, not the array.
06Assignment shares, copying duplicates
int[] b = a; copies the reference, so both names lead to one array: b[0] = 100 changes a[0] too. This is the aliasing you saw in Topic 3.3.
To get an independent array, copy it: a.clone() (same length), Arrays.copyOf(a, newLength) (pads with defaults or truncates), or System.arraycopy(src, srcPos, dest, destPos, count) to copy part of one array into another.
Arrays can't grow. "Growing" means creating a bigger array and copying the old elements in, which is exactly what ArrayList does for you when it fills up (Topic 9.2).
07Arrays are objects with a type
Every array is an object whose class is created by the JVM: int[] has the internal name [I, String[] is [Ljava.lang.String;, and int[][] is [[I. That's what you see when you print an array directly: [I@87aac27 is the type name, @, and a hash code in hex.
Object arrays are covariant: a String[] can be assigned to an Object[] variable. That's convenient but unsafe, so the JVM checks every store into an object array at run time and throws ArrayStoreException if the type is wrong. Generic collections (Phase 8) avoid this hole.
Try it yourself
- 1
Reverse an array in place
Write a loop that swaps
arr[i]witharr[arr.length - 1 - i]foriup to the middle. Predict what happens with an odd length (the middle element stays put), then run it on{1, 2, 3, 4, 5}and print withArrays.toString. - 2
Print an array the wrong way
Add
System.out.println(scores);to the first example. You'll see something like[I@87aac27, and the part after@changes between runs. Replace it withArrays.toString(scores)(addimport java.util.Arrays;). - 3
Grow an array by hand
Start with
int[] data = {1, 2, 3};. Write the three lines that "add" a4: make a new array one longer, copy, set the last element, and pointdataat the new array. This is whatArrayList.adddoes when full.
Code & diagrams
Expected output
defaults: 0 0.0 false null
length = 5
sum = 402, average = 80.4, max = 95
last prime = 11
2 3 5 7 11Expected output
a = [100, 2, 3]
alias = [100, 2, 3]
copy = [1, 2, 3]
a == alias: true
a == lookalike: false
Arrays.equals(a, lookalike): true
bigger = [100, 2, 3, 0, 0]
dest = [0, 100, 2, 3, 0]Expected output
[I
[Ljava.lang.String;
[[I
ArrayStoreException: java.lang.Integer
NegativeArraySizeException: -1Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Loop with <= instead of <
Write i <= scores.length in the loop condition.
public class Main {
public static void main(String[] args) {
int[] scores = {90, 85, 70, 60, 95};
for (int i = 0; i <= scores.length; i++) {
System.out.println(scores[i]);
}
}
}Break #2
Call length() on an array
Write arr.length() as you would for a String.
int[] a = {1};
int x = a.length();Break #3
Print an array directly
Write System.out.println(scores);.
Myth vs fact
Myth
int[] b = a; makes a copy of the array.
Fact
It copies the reference. Both names refer to the same array. Use clone() or Arrays.copyOf to copy.
Myth
Arrays can grow when you add elements.
Fact
Their length is fixed forever. "Growing" means allocating a bigger array and copying, which ArrayList does automatically.
Myth
a == b compares array contents.
Fact
It compares references. Arrays.equals(a, b) compares contents element by element.
Myth
Array elements are uninitialised garbage like in C.
Fact
Every element starts at its default value: 0, false, the zero char or null.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
On 64-bit HotSpot with compressed class pointers, an array object has a 16-byte header (12-byte object header plus a 4-byte length), then the elements, rounded up to a multiple of 8 bytes. So
new int[1000]is 4016 bytes, while anInteger[1000]holding distinct values costs 4016 bytes of references plus 16 bytes perIntegerobject. JDK 25 can shrink object headers further with-XX:+UseCompactObjectHeaders. - ▸
The maximum length is a little under
Integer.MAX_VALUEbecause of header space. Asking fornew long[Integer.MAX_VALUE]fails withOutOfMemoryError: Requested array size exceeds VM limitregardless of heap size. - ▸
Array covariance (
String[]is anObject[]) is why every store into an object array needs a run-time type check, and why you can't create generic arrays likenew T[10]. Collections chose the opposite rule:List<String>is not aList<Object>(Topic 8.5). - ▸
Arrays of primitives store values contiguously, which makes scanning them very cache-friendly. An
Integer[]orList<Integer>stores references to objects scattered around the heap, which can be several times slower to scan and much larger. For number crunching, prefer primitive arrays.
Remember this
- 1
An array holds a fixed number of elements, all of one type:
int[] scores = new int[5];makes room for fiveints. The number of elements is its length, read withscores.length(a field, no brackets), and it can't change after creation. - 2
Elements are numbered by index, starting at 0. The first is
scores[0], the last isscores[scores.length - 1]. Using any other index throwsArrayIndexOutOfBoundsExceptionat run time: Java checks every access. - 3
newfills a fresh array with default values:0for numbers,falseforboolean, the character with code 0 forchar, andnullfor object types likeString. With an array initializer,int[] primes = {2, 3, 5, 7};, you choose the values and Java counts the length. - 4
An array is an object on the heap, and the variable holds a reference to it (Topic 3.3). So
int[] b = a;doesn't copy anything:aandbare two names for one array. To copy, usea.clone(),Arrays.copyOf(a, n)orSystem.arraycopy. - 5
Because arrays are objects,
==asks *same array?*, not *same contents?*, and printing one shows something like[I@87aac27(its type code and a hash). UseArrays.equals(a, b)andArrays.toString(a)instead (Topic 3.10). - 6
Arrays are the foundation of almost every data structure:
ArrayList(Topic 9.2),HashMap(Topic 9.4) andArrayDequeare built on arrays inside. The DSA course's Arrays module (/dsa/arrays) is where you put them to work on problems.
Explain it without notes
Why do array indexes start at 0, and what is the valid index range of an array?
What happens in memory when you write int[] b = a;, and how do you make a real copy?
What does System.out.println(arr) print and why? How do you print the contents?
What is array covariance, and what run-time error can it cause?
Practice
Write a program that counts how many numbers in {4, -2, 7, 0, -5, 3} are positive, negative and zero.
Write static int indexOf(int[] arr, int target) that returns the first index of target, or -1. Test with {5, 8, 2, 8} and targets 8 and 9.
Write static int[] reversed(int[] arr) that returns a new reversed array, leaving the input unchanged. Print both.
Write a program that shifts every element of {1, 2, 3, 4, 5} one place to the left, moving the first element to the end, giving [2, 3, 4, 5, 1].
Trade-offs
- ↔
Arrays give the fastest possible indexed access and the most compact storage, but their size is fixed. When the number of items changes, an
ArrayListhandles resizing for you at a small cost. - ↔
Primitive arrays (
int[]) are compact and fast; object arrays and collections of wrappers (Integer[],List<Integer>) are more flexible but use several times more memory. - ↔
Sharing an array between methods avoids copying but invites accidental changes. Copy at boundaries when the data must not change.
Done when you can
Done when you can create arrays with
newand with initializers, and know their default values.Done when you can loop over an array with both loop styles without going out of bounds.
Done when you can explain why
int[] b = a;doesn't copy and make a real copy.Done when you can compare and print arrays correctly with
Arrays.equalsandArrays.toString.Done when you can read an
ArrayIndexOutOfBoundsExceptionand fix it.