arrays-and-algorithms 17 min read

Arrays: A Row of Variables That Share a Name

Fixed size, one type, indexed from zero

In a nutshell

An array is a single variable that holds a fixed-size row of values, all the same type, accessed by a number called an index. One name, many slots, all the same shape. Indices start at 0 and end at length - 1. Once an array is created its length is locked: to “grow” an array, you build a new one and copy.

Today’s job is to picture an array clearly enough that the syntax stops getting in the way. Three concrete things to walk away with:

  1. The mental model: a row of numbered boxes, all the same size, sharing one name.
  2. Three ways to make an array, plus what each slot starts at if you do not assign it.
  3. The difference between arr (the whole row) and arr[i] (the value sitting in slot i).

Today in three sentences. An array is a row of variables that share a name. arr is a reference; arr[i] is what is in slot i. Length is fixed at creation; to grow or shrink, build a new array.

After this lesson, you will be able to:

  • Declare and create an array three different ways: new int[5], an initializer list {3, 1, 4}, and the verbose form new int[]{...}.
  • State the default value Java puts in every slot for each primitive type and for reference types.
  • Read arr and arr[i] correctly: tell which is the whole-array reference and which is a single slot’s value.
  • Use arr.length to get an array’s size (and remember it is a field, not a method).

From CSCD 110. A Python list looks superficially similar ([1, 2, 3] is a row of values you index with nums[0]), but two things differ. (1) Python lists grow: you can append. Java arrays do not. (2) Python lists hold mixed types: [1, "hi", 3.14] is fine. Java arrays are typed: int[] holds only ints, String[] holds only Strings. The closer Python analogue to a Java int[] is array.array('i', ...) or numpy.zeros(n, dtype=int).


The 100-variable problem

Suppose you need to store 100 test scores. Without arrays, you would write something like this:

int score1 = 85;
int score2 = 92;
int score3 = 78;
// ...97 more variables...
int score100 = 73;

That is absurd for three reasons. You cannot loop over them (the loop variable would have to compose a different identifier each iteration, which Java does not allow). You cannot pass them to a method as one thing (you would need a 100-parameter method header). And if the count changes from 100 to 101 next quarter, you rewrite all the code that touched them.

An array packages those 100 variables into one. One name. One declared type. A fixed number of slots, all next to each other, addressed by an index that runs from 0 to length - 1.

int[] scores = new int[100];   // 100 int slots, all starting at 0
scores[0]  = 85;                // first slot
scores[99] = 73;                // last slot (NOT scores[100])

Picture it. The variable scores names a single object. Inside that object are 100 numbered cells, each big enough to hold one int. The cell numbers run 0, 1, 2, …, up to 99. There is no cell number 100, and trying to read or write scores[100] will crash the program.

        +----+----+----+----+----+----+ ... +----+
scores  | 85 |  0 |  0 |  0 |  0 |  0 | ... | 73 |
        +----+----+----+----+----+----+ ... +----+
          0    1    2    3    4    5         99

A useful way to read the picture: the index i is the slot number, and scores[i] is what is in that slot. They are two different kinds of value. Mixing them up is one of the most common beginner array bugs, and you will see it again in the next lesson.

Common pitfall: indexing from one. Many students have used spreadsheets where rows are numbered 1, 2, 3. Java arrays start at 0. If scores has 100 elements, the legal indices are 0, 1, 2, ..., 99. The value scores[100] does not exist; reading it throws ArrayIndexOutOfBoundsException. The very last element is always arr[arr.length - 1], never arr[arr.length].

Check your understanding. You declare int[] grades = new int[10]. Which of these expressions are legal (will not throw)?

A. grades[0] B. grades[10] C. grades[9] D. grades[-1]

Reveal answer

A and C are legal. B is one past the end (grades has indices 0..9); accessing it throws ArrayIndexOutOfBoundsException. D is also illegal — Java arrays do not allow negative indices, and accessing grades[-1] throws the same exception.


Three ways to make an array

There are three syntactic forms, all common in Java code. Read all three; you will see all three.

Form 1: new with a size. Use this when you know the size but do not yet have the values, or when you plan to fill the array in a loop.

int[] a = new int[5];     // 5 int slots, all initialized to 0
double[] temps = new double[365];   // 365 double slots, all 0.0
String[] names = new String[10];    // 10 String slots, all null

Form 2: an initializer list. Use this when you know the values up front. Curly braces, comma-separated, type inferred from the array’s declared type.

int[] primes  = {2, 3, 5, 7, 11};
String[] days = {"Mon", "Tue", "Wed", "Thu", "Fri"};
double[] gpa  = {3.7, 3.2, 4.0};

The compiler counts the elements for you, so you do not write a size. You also cannot use the initializer-list shortcut except as part of a declaration; this does not compile:

int[] primes;
primes = {2, 3, 5};        // compile error: only allowed at declaration

Form 3: new with an initializer list. The verbose long-form of Form 2. You can use this anywhere you would use a value, including as an argument to a method or after the declaration line.

int[] primes = new int[]{2, 3, 5, 7, 11};
methodThatTakesArray(new int[]{1, 2, 3});   // legal here, where {1,2,3} alone wouldn't be

You will use Form 1 most often (allocate, then fill in a loop), Form 2 when you have a small literal, and Form 3 when you need to pass an inline array to a method.

What each slot starts at

When Java creates an array with new, it fills every slot with that type’s default value. You do not get garbage; you get a known, predictable starting value.

Slot type Default value Notes
int, long, short, byte 0 Integer zero.
double, float 0.0 Floating-point zero.
boolean false  
char '\u0000' The null character; prints as nothing visible.
String null And every other reference type.
Any other reference type null  

The reference-type default is the one that bites first. new String[10] does not give you ten empty strings. It gives you ten nulls. Calling a method on any one of them throws NullPointerException.

String[] names = new String[3];
System.out.println(names[0].length());   // NullPointerException

Until you assign each slot a real String, those slots are unusable. Either assign them in a loop, or use an initializer list ({"Ada", "Linus", "Margaret"}) to fill them at creation.

Common pitfall: assuming new String[n] gives empty strings. It gives nulls. The same is true for any reference-type array: new int[n][] gives an array of null arrays, new Person[n] gives an array of null references. Always assign before reading, or use Form 2 to fill at creation.

Check your understanding. What does each of these print?

A. int[] a = new int[3]; System.out.println(a[1]); B. boolean[] b = new boolean[2]; System.out.println(b[0]); C. int[] c = {10, 20, 30}; System.out.println(c[2]);

Reveal answer

A. Prints 0. Default for int. B. Prints false. Default for boolean. C. Prints 30. Initializer-list values fill the slots in order, so c[0]=10, c[1]=20, c[2]=30.


arr vs arr[i] (and why .length has no parens)

This is the one mental-model split that pays for itself a hundred times this week.

The variable arr (no brackets) names the whole array. It holds a reference to the array object. If you print it, Java does not print the elements — it prints something cryptic like [I@5e91993f, which is the type tag plus a hex address. That output is the address of the array object on the heap.

The expression arr[i] (with brackets and an index) names the value in slot i. It is a single value of whatever type the array holds. If arr is int[], then arr[i] is an int.

Concretely:

int[] arr = {7, 2, 9, 4};

System.out.println(arr);      // [I@5e91993f   (a reference, not the elements)
System.out.println(arr[0]);   // 7
System.out.println(arr[3]);   // 4
System.out.println(arr.length); // 4

The expression arr[i] works as both a read and a write:

int x = arr[2];        // read: x is now 9
arr[2] = 99;           // write: slot 2 is now 99
arr[2]++;              // also a write: slot 2 is now 100

Whenever you read a loop, ask which one you are looking at. i is the slot number. arr[i] is what is in the slot. Confusing them is the dominant source of array bugs in CS1.

arr.length is a field, not a method

Every Java array exposes its size as a field named length. That means no parentheses.

int[] arr = {3, 1, 4, 1, 5, 9};

arr.length            // 6     correct
arr.length()          // compile error: ()'s are for methods

This is annoying because the very similar-looking String exposes its size as a method called length() (with parens), and ArrayList (which you’ll meet in CS2) uses yet another name, size(). You will mistype this several times before it sticks.

String s = "hello";
s.length();            // 5    method call, parens required

int[] arr = {1, 2, 3};
arr.length;            // 3    field access, no parens

The bigger picture: arrays in Java are not regular objects, and length is a special property they expose without going through a method. You do not need to remember the design rationale; you do need to remember the syntax.

Common pitfall: writing arr.length(). It is the most common compile error students hit during the first array lab. The compiler error is cannot find symbol: method length(). The fix is one keystroke: delete the ().

Check your understanding. Given int[] arr = {10, 20, 30}, which of these are legal? For the illegal ones, what is the error?

A. arr.length B. arr.length() C. arr[arr.length] D. arr[arr.length - 1]

Reveal answer

A. Legal. Evaluates to 3. B. Compile error: length is a field, no parentheses. C. Compiles, but throws ArrayIndexOutOfBoundsException at runtime. The legal indices are 0..2; index 3 is one past the end. D. Legal. Evaluates to arr[2], which is 30. This is the standard idiom for “the last element.”

Check your understanding. Predict the output.

int[] arr = new int[4];
arr[1] = 10;
System.out.println(arr[0] + " " + arr[1] + " " + arr[2] + " " + arr[3]);
Reveal answer

Output: 0 10 0 0. new int[4] creates four slots all initialized to 0. The line arr[1] = 10 overwrites slot 1. The other three slots stay at the default 0.


Wrap up and what’s next

Recap.

  • An array is a single object holding a fixed-size row of values, all of the same declared type.
  • Indices run from 0 to length - 1. There is no slot at arr[arr.length].
  • Three creation forms: new int[5] (size, default-filled), {3, 1, 4} (initializer list at declaration), new int[]{1,2,3} (verbose, usable as an expression).
  • Default slot values: 0 for integer types, 0.0 for floating-point, false for boolean, '\u0000' for char, and null for every reference type. new String[10] is ten nulls, not ten empty strings.
  • arr is the whole-array reference. arr[i] is the value in slot i. arr.length is the size as a field, no parens.

What you can do now. Read array-creation code and predict what is in every slot before any explicit assignments. Spot the arr.length() typo immediately. Tell arr and arr[i] apart in any expression you read.

Next up: Walking an Array (and the Aliasing Trap). The canonical for-loop traversal pattern. The off-by-one bug that produces the bounds exception. The enhanced for (int v : arr) form and when it does (and does not) do what you want. Accumulator patterns from Week 4 (sum, max, min, count) reused on arrays. And the first place where pass-by-value tells a more interesting story: aliasing.


  • Reges & Stepp, Building Java Programs, Chapter 7 sections 7.1 and 7.2 cover declaration, indexing, and length.
  • FAQ entries on ArrayIndexOutOfBoundsException and on the difference between arr.length and s.length().