Array Basics and Indexing
Parallel arrays store one data item across several arrays, and the same index selects that item's value in each array.
Why Programs Need Collections
Many programs must track many items at once. Consider a program that draws the same message 25 times on the screen. Each copy appears at its own location, in its own color and font, and each one drifts across the window at its own speed. Every copy therefore needs a set of facts: where it is, how fast it moves horizontally and vertically, what color it has, and which font it uses. The question this article answers is how a program should organize those facts so that all 25 copies can be tracked at the same time.
A related problem appears even in a simpler setting. Suppose a program must process the names of one thousand people. The natural first thought is to create one thousand variables to hold the names and one thousand print statements to display them. That approach becomes unworkable very quickly. An array solves this problem by keeping the whole list under a single name, and it lets you reach any single item through its position in that list.
Arrays as Numbered Sequences
An array is a data structure whose items are arranged as a numbered sequence. One array variable holds a whole list, and each item can be reached by its position number.
Two rules shape arrays in Java. First, every item in the array must be of the same type. Second, the numbering always begins at zero. The source notes that some other programming languages do not impose the same-type rule, so this is a property of the Java setting rather than a property of arrays in general.
Three terms describe any array, and keeping them separate is the key to reading array descriptions correctly. The length is the number of items the array contains. The base type is the type shared by the individual items. The index is the position number of one particular item.
Because positions are numbered from zero, an array of length n has indices that run from 0 to n minus 1. The highest index is always one less than the length. A learner who treats the first position as index 1 has made the most common indexing error, and that error is addressed later in this article.
Length, Base Type, and Index
Reading a five-name array
Given the array String[] names = {"Ada", "Brook", "Chen", "Dana", "Eli"}, identify its length, its base type, and the index of the item Chen.
- 1Find the length
The array contains five items, so its length is 5.
- 2Find the base type
Every item is a String, so the base type is String.
- 3Find the index of Chen
Numbering begins at zero. Ada is at index 0, Brook at index 1, and Chen at index 2. The index of Chen is 2.
- 4Check the highest index
The indices run from 0 to 4, which is one less than the length of 5. Eli is at index 4.
Length 5, base type String, Chen is at index 2, and the valid indices are 0 through 4.
Read the diagram from left to right. The first name sits at index 0 and the last name sits at index 4. The length is 5 even though the last index is 4. Each slot holds one value of the base type, which here is String.
Elements Are Variables, Not Their Values
An array of ints can be understood as a list of variables of type int, and an array of Strings can be understood as a list of variables of type String. The source is explicit that an array is not, properly speaking, just a list of integers or strings. It is better thought of as a collection of variables, each of which can hold a value of the array's base type.
The individual variables that make up an array are called its elements. Each element can hold a value of the base type, and the value of an element can be changed at any time, just as the value of an ordinary variable can be changed. An element is therefore a storage position. The value currently stored in that position is separate from the position itself.
| Question | The element (the slot) | The value in the slot |
|---|---|---|
| What is it? | A storage position with a fixed index | The data currently stored in that position |
| Can it change? | It stays at the same index for the life of the array | It can be replaced with another value of the base type |
| Example | names[1] | Ben, or Zoe after the value is replaced |
Distinguishing an element from the value it holds
What do you think happens?
If names[1] changes from Ben to Zoe, is index 1 still the same position in the array?
Reveal answer
Yes. The position at index 1 is unchanged. Only the value stored in that element changed from Ben to Zoe.
Each element is a storage position that can hold a value of the base type. Replacing the value leaves the slot in place.
Parallel Arrays Link Data by Index
The first idea most programmers reach for is to give each fact its own array. For the 25 copies of the message, the program can declare six arrays, each holding 25 values. One array holds the horizontal coordinates, one holds the vertical coordinates, two hold the velocity components, one holds the colors, and one holds the fonts. The velocity arrays are not needed for drawing at all. They are read later, when the positions are updated for the next frame.
The drawing code reads all of these arrays with one shared index. The string at position i is drawn at the point given by x[i] and y[i], takes its color from color[i], and uses the font stored at font[i]. Nothing in the arrays themselves says that position 2 in x belongs with position 2 in y. The link exists only because the program always uses the same index for each array. This arrangement is called a set of parallel arrays.
The diagram shows only three of the positions, but the pattern continues for every index. Each array is independent in storage. The only thing connecting Cruz, age 20, and score 88 is that all three sit at index 2 in their respective arrays.
What Breaks When One Array Changes
Because the connection between arrays is carried only by the index, a single edit to one array can silently break the connection. The following program stores three students in two parallel arrays, names and scores. It then swaps only the names of the first and third students, leaving the scores array untouched.
String[] names = {"Ana", "Ben", "Cruz"};
int[] scores = {90, 75, 88};
for (int i = 0; i < names.length; i++) {
System.out.println(names[i] + " scored " + scores[i]);
}
String temp = names[0];
names[0] = names[2];
names[2] = temp;
for (int i = 0; i < names.length; i++) {
System.out.println(names[i] + " scored " + scores[i]);
}- 1Lines 1 and 2 declare the two parallel arrays. Index 0 links Ana to 90 and index 2 links Cruz to 88.
- 2The first loop reads both arrays with the same index i, so each printed line pairs a name with its own score.
- 3The temp variable holds the name at index 0 while the two name slots are exchanged.
- 4After the swap, index 0 holds Cruz but scores[0] still holds 90, so Cruz is now paired with Ana's score.
- 5The second loop shows the drift. Nothing reports an error, but the output is wrong.
- The first loop prints the correct pairings, because index i selects the same student in both arrays.
- The three-line swap changes only the names array. The scores array still holds 90, 75, 88 in the same positions.
- The second loop prints Cruz with 90 and Ana with 88. The name at each index changed, but the score did not, so the pairing is wrong.
Ana scored 90
Ben scored 75
Cruz scored 88
Cruz scored 90
Ben scored 75
Ana scored 88This is the central debugging lesson of parallel arrays. When a printed result does not match what you expected, look for every statement that writes to any one of the arrays. For each one, ask whether the rest of the set changed with it. If the answer is no, the arrays have drifted out of alignment.
Common Indexing Mistakes
Common mistake Updating one parallel array without the others
What it looks likeSwapping names[0] and names[2] while leaving scores unchanged.
Why it failsThe link between a name and its score exists only through the shared index, so changing one array alone breaks the link.
Do this insteadWhenever an index is moved or rewritten in one array, apply the same change at that index in every array of the set.
Common mistake Treating the first position as index 1
What it looks likeBelieving the first name in names is names[1].
Why it failsNumbering always begins at zero, so the first position is index 0.
Do this insteadFor an array of length n, use indices 0 through n minus 1.
Common mistake Using the length as the last index
What it looks likeReading names[5] from an array of length 5.
Why it failsThe indices of an array of length 5 run from 0 to 4, so index 5 does not exist.
Do this insteadThe highest valid index is always the length minus 1.
Common mistake Confusing an element with its value
What it looks likeThinking that replacing the value at names[1] creates a new position.
Why it failsAn element is a storage position with a fixed index. Only the value stored in it changes.
Do this insteadDescribe the change as a new value in the same slot.
Common mistake Using an element of an object array before the element has been created
Grouping Fields in One Object
The source program does not keep six separate arrays for the strings. It points out that parallel arrays work in this simple example, but that they go against the object-oriented habit of keeping related data together in one object. If all the data for one copy is stored in one object, the relationship between the values never has to be imagined, because all of it sits in one place. The author therefore defines a small class that holds everything needed to draw one string.
Each object of this class is a complete record for one copy of the message. The fields have the same names as the parallel arrays did, but they now belong to a single item. The program refers to the fields of one object rather than to matching positions in several arrays.
Reading Elements with a For-Each Loop
Once the array of objects is filled, the strings can be drawn with an ordinary counting loop, in which each iteration uses the index to pick one object. The source program uses an equivalent for-each loop, which the author describes as easier to understand. The loop control variable named data takes on one element of the array on each pass, in order from the first index to the last.
What do you think happens?
Given String[] names = {"Ada", "Brook", "Chen"}, what does the loop variable hold on the second pass of a for-each loop over names?
Reveal answer
Brook
The loop visits the elements in index order, so the second pass holds the element at index 1, which is Brook.
Because the elements are references to StringData objects, data gives direct access to the fields of that copy. The array index no longer appears in the loop. The loop variable holds the element itself, not a copy of its value, which is why the fields of data can be read directly.
Practice Tracing Arrays
An array holds String[] colors = {"red", "blue", "green", "gold"}. State its length, its base type, and the index of blue. Then state the highest valid index.
Hints
- Count the items to get the length.
- Numbering starts at zero, so the first item is at index 0.
- The highest valid index is the length minus 1.
Two parallel arrays are String[] names = {"Ana", "Ben", "Cruz"} and int[] scores = {90, 75, 88}. You reorder only scores so that it reads {75, 88, 90}. Predict which student is now paired with 90, and explain how you would repair the set so that every pairing is correct again.
Hints
- Compare each index of the reordered scores with the unchanged names array.
- Every write to one parallel array must be matched by the same write to the others.
Key Takeaways
- An array is a numbered sequence of items that share one base type, with indices starting at zero and running from 0 to length minus 1.
- Each element is a storage position that can hold a value of the base type. The value can change, but the position stays the same.
- Parallel arrays store one data item across several arrays, and the same index selects that item's value in each array. Updating one array without the others breaks the link between values.
- An array of objects needs two levels of creation: the array itself and then each element, before its fields can be used.
- A for-each loop visits each element in index order, and the loop variable holds the element itself on each pass.