Creating and Initializing Arrays
A parameter may have an array type such as int[], so one argument can deliver an entire array to a subroutine.
Predicting the Output Before Reading On
Suppose you want a subroutine that prints every value in an integer array in a tidy format: the values separated by commas and enclosed in square brackets. The subroutine needs to know which array to print, and in Java the way to supply that information is through a parameter. This article looks at what happens when the type of a parameter is itself an array type, so that a whole collection of values travels into the subroutine as one argument.
What do you think happens?
A caller creates an integer array holding the values 42, 17 and 256, then calls the list-printing subroutine on it. Write the exact text that appears on the screen, including where the brackets and commas fall.
Reveal answer
[42, 17, 256]
The subroutine in the example below prints an opening bracket, then each value followed by a comma only when another value comes after it, and finally a closing bracket. The commas and brackets are not automatic; they come from the explicit code in the loop, which is why their placement has to be traced pass by pass.
Keep your prediction in mind. The rest of this article shows how the array reaches the subroutine, how the loop reads its elements, and where a printed result can drift away from what you expected.
Declaring an Array Parameter
A parameter's type is normally a simple type such as int, but it can also be an array type. Writing int[] as the parameter type means the subroutine accepts an integer array. The name after the type is the name the subroutine uses for the array inside its own body. In the subroutine below, the name is list, so every reference to list in the body refers to the array that was passed in.
- int[] is the parameter type: an array of integers, not a single int
- list is the name the subroutine uses for that array inside its body
- The subroutine does not need to know the name of the caller's variable
The difference from an ordinary int parameter is the amount of information that one argument carries. An int parameter receives a single value. An int[] parameter receives an entire collection of values in one argument, and the subroutine can then reach each position inside it.
Creating the Array Before the Call
A subroutine cannot be called with an array that does not exist yet. The array must be created and initialized first, and only then can it be passed as the argument. The source illustrates this with a short segment that declares an array variable, creates an array of three integers, fills in its elements and then makes the call. The source itself describes such code as legal but not very realistic, since the array exists only to be handed to the subroutine.
- 1Declare an array variable, here named numbers, that will refer to an array of integers.
- 2Create an array of three integers and make numbers refer to it.
- 3Assign values to the positions of the array, one position at a time.
- 4Make the call
printValuesInList( numbers ), passing the array as a single argument.
The three assignments fill positions 0, 1 and 2. Positions in Java arrays are numbered starting from zero, so the first value is at index 0 and the last value is at index 2. This numbering matters again when the subroutine walks through the array.
[42, 17, 256]- main creates the array and fills positions 0, 1 and 2 before the call
printValuesInList( numbers )is the single argument that delivers the whole array- The loop never names 42, 17 or 256
One Argument, the Same Array
When the call printValuesInList( numbers ) runs, the name numbers appears in the argument position, and the subroutine receives the array as its list parameter. The subroutine does not need to know the name numbers. It works with the array it was given through list. The diagram below shows the two names side by side: the caller's variable and the subroutine's parameter both lead to the same array object.
Because both names lead to the same array, a change made through list is a change to the array the caller holds as numbers. The subroutine in this article only reads elements, but the same fact explains why the handoff is so powerful: a single argument gives the subroutine access to the whole collection, not a private copy of one value.
Reading Elements with an Index and Length
Inside the subroutine, an element is reached by writing the array name followed by an index in square brackets, as in list[i]. The index says which position to read. The subroutine also needs to know how many positions exist, and it uses the length property of the array to find out. Together these two ideas form the loop that prints the values one at a time.
For an array of length 3, the valid indexes are 0, 1 and 2, which means the last valid index is length minus one. Index 3 is the position one past the end. A loop that reads list[i] must stop before it reaches index length, because there is no element stored at that position.
for (int i = 0; i < list.length; i++) {
System.out.print(list[i]);
if (i < list.length - 1) {
System.out.print(", ");
}
}- 1int i = 0 starts the index at the first position, which is zero in Java.
- 2i <
list.lengthis the test that keeps the loop running; it stops once i reaches the length. - 3i++ moves the index to the next position after each pass.
- 4
System.out.print(list[i])prints the element stored at the current index. - 5The if statement prints a comma only when another element follows, which is where the separators come from.
The loop never names the values 42, 17 or 256. It works for any array of integers, because it reads the length and the elements from whatever array it receives. This is the main benefit of an array parameter: one subroutine serves every array that has the right type.
Tracing the Printing Loop Pass by Pass
Now check your earlier prediction by following the program one step at a time. Trace the state of the index, the comma test and the text printed so far. Watch how the array moves from main into printValuesInList, and where each piece of the printed text is produced.
Tracing printValuesInList on [42, 17, 256]
main creates an array holding 42, 17 and 256 and calls printValuesInList( numbers ). Determine the text printed by the subroutine, pass by pass.
- 1Before the loop
The subroutine prints an opening bracket, so the output so far is [.
- 2Pass 1, i = 0
The test 0 < 3 is true. list[0] is 42, so 42 is printed. The test 0 < 2 is true, so a comma and space follow. Output so far: [42, .
- 3Pass 2, i = 1
The test 1 < 3 is true. list[1] is 17, so 17 is printed. The test 1 < 2 is true, so a comma and space follow. Output so far: [42, 17, .
- 4Pass 3, i = 2
The test 2 < 3 is true. list[2] is 256, so 256 is printed. The test 2 < 2 is false, so no comma follows. Output so far: [42, 17, 256.
- 5Loop exit, i = 3
The test 3 < 3 is false, so the loop ends. The subroutine prints the closing bracket and a line break.
The printed text is [42, 17, 256], which matches the prediction. Each comma comes from the if test on the pass before the last, not from the array itself.
Mistakes When Tracing Array Subroutines
Common mistake Calling the subroutine before the array is created and filled
What it looks likePassing numbers to printValuesInList before the lines that assign numbers[0], numbers[1] and numbers[2] have run.
Why it failsA subroutine cannot be called with an array that does not exist yet, so the call has nothing complete to deliver.
Do this insteadCreate and initialize the array in the calling code first, then make the call with it as the single argument.
Common mistake Assuming the subroutine needs the caller's variable name
What it looks likeWriting list = numbers inside the subroutine body and expecting the name numbers to be visible there.
Why it failsThe subroutine receives the array through its own parameter name, list. It works with that name and does not need to know numbers.
Do this insteadRefer to the array only through the parameter name, as in list[i] and list.length.
Common mistake Looping to the length instead of stopping before it
What it looks likeWriting for (int i = 0; i <= list.length; i++), so the loop tries to read index 3 in a three-element array.
Why it failsValid indexes run from 0 to length minus one. Index length is past the last stored element.
Do this insteadUse i < list.length as the loop test.
Common mistake Printing the comma unconditionally
What it looks likeRemoving the if test so every pass prints a comma, which produces [42, 17, 256, ].
Why it failsThe comma is produced by explicit code in the loop, and this version runs it after the last value too.
Do this insteadPrint the separator only when another element follows, using i < list.length - 1.
Common mistake Assuming the printed commas and brackets are stored inside the array
Practice Tracing a Different Array
Using the same printValuesInList subroutine, suppose main creates an array of three integers and assigns 5 to position 0, 9 to position 1 and 5 to position 2, then makes the call. Write the exact printed text, and then say which pass, if any, prints a comma with nothing following it.
Hints
- Start with the opening bracket, then trace i = 0, 1 and 2 in order.
- The comma test is i <
list.length- 1, which is only true on the first two passes. - Ask yourself whether the value 5 at index 2 changes any comma decision.
Key Takeaways
- A parameter may have an array type such as int[], so a single argument can deliver an entire array to a subroutine.
- The array must be created and initialized in the calling code before it is passed, and the subroutine works with it through its own parameter name.
- Inside the subroutine, list[i] reads the element at index i, and
list.lengthbounds the loop so that indexes run from 0 to length minus one. - Commas and brackets in the printed output come from explicit code in the loop, so their placement must be traced pass by pass when a result departs from expectation.