Creating, Accessing, Copying, and Traversing Arrays

Storing many values of one type in a single variable: creating arrays, selecting elements with the bracket operator, why printing an array gives you an address, the difference between copying a reference and copying an array, and the three traversal patterns you will use for the rest of the book. Follows Think Java 2e, Chapter 7 (Arrays and References), Sections 7.1-7.5, pp. 107-115, cross-referenced against The Java Tutorials — Arrays.

Subject: Java · 65 slides · code lesson

Open the interactive version of this deck

What this lesson covers

The lesson, slide by slide

1. Creating, Accessing, Copying, and Traversing Arrays

Title

Think Java 2e · Chapter 7 · Arrays and References

Sections 7.1-7.5 · pp. 107-115

2. What you will be able to do

Objectives

This lesson follows Think Java 2e, Chapter 7 (Arrays and References), Sections 7.1-7.5, pp. 107-115. Everything on these slides can be checked against those pages.

1. Declare an array variable and create an array with new, or with an initializer list.

2. Select and assign elements with the bracket operator, and say what the valid indexes are.

3. Explain what an array variable actually holds, and draw the reference as an arrow.

4. Distinguish copying a reference from copying an array, and define aliasing.

5. Use a.length correctly, and say why it has no parentheses when String.length() does.

6. Write the three traversal patterns: transform every element, search for one, and reduce to a single value.

3. Retrieve before you read

Warm-up

A problem from the last chapter that arrays exist to solve.

Discussion prompt

To check whether every letter in a string appears exactly twice, one approach loops through the string 26 times — once per letter. For a 3-million-character book, roughly how many steps is that? What would you need in order to do it in one pass?

Hint: 26 times 3 million.

Answer:

About 80 million steps. Doing it in one pass would need 26 separate counters — one per letter — updated as you go.

But who wants to declare 26 variables? That is exactly where arrays come in: a single variable that stores 26 integers, with arithmetic to pick out the right one. The end of the next lesson writes that program.

4. One variable, many values

Concept

Up to now every variable has held one value. An array holds a sequence of values of the same type, each identified by an index — which is what makes it possible to write programs that manipulate larger amounts of data.

Figure (svg): An int array of four elements, all initialised to zero, with indexes 0 to 3 underneath

Downey & Mayfield, Think Java, 2nd edition (Green Tea Press / O'Reilly, 2020) — Think Java 2e, Chapter 7 (Arrays and References), Sections 7.1-7.5, pp. 107-115 — Chapter 7 opens on printed page 107.

5. Creating arrays

Section

Section 7.1

6. Declare the variable, then create the array

Concept

An array is a sequence of values; the values are called elements. You can make an array of ints, doubles, Strings or any other type — but all the values in an array must have the same type.

int[] counts;                  // declare an int array variable
double[] values;

counts = new int[4];           // create the array itself
values = new double[size];

int[] counts = new int[4];     // or both in one line
partwhat it does
int[]the type: an array of ints — note the square brackets
countsthe variable name
new int[4]allocates memory for four ints and initialises them all to zero
new double[size]the size can be any int expression, decided when the array is created

array — A collection of values in which all the values have the same type, and each is identified by an index.

element — One of the values in an array.

Array types look like other Java types, except they are followed by square brackets. The new operator — which you first met creating a Scanner — allocates the memory and automatically initialises all the elements to zero.

7. Three ways to make an array

Notation

The declaration and the creation are separate steps, and there is a shorthand that does both with specific values.

Annotate

  • The declaration creates no array. int[] a; makes a variable that could refer to an array. Until you assign one, it refers to nothing.
  • new int[4] allocates the memory and zeroes it. Every element starts at 0 for numeric types — you never get whatever happened to be in memory.
  • The size can be any int expression, as long as it is non-negative. It is fixed at the moment the array is created and cannot change afterwards.
  • A negative size throws NegativeArraySizeException. An array with zero elements is allowed, and there are special uses for such arrays.
  • The brace form initialises with specific values: {1, 2, 3, 4} creates an array of four elements and fills them. The size is however many values you listed.

The brace form only works in a declaration. Once the variable exists you must use new — which is a restriction worth knowing before you meet it.

8. Choosing the size at run time

Worked example

The size does not have to be a literal. That is what makes arrays useful for data whose amount you do not know when you write the program.

Scanner in = new Scanner(System.in);
System.out.print("How many scores? ");
int size = in.nextInt();

int[] scores = new int[size];
System.out.println("Created an array of " + scores.length);
the user typessizearray createdscores.length
55five elements, all 05
00an empty array — legal0
-1-1NegativeArraySizeException—

Read the size before creating the array.

Why: new int[size] uses whatever value size holds at that moment.

Note that the size is fixed from then on.

Why: An array cannot grow. If you need more room you create a new, larger array and copy.

Note that zero is allowed.

Why: An empty array is a perfectly good array — it just has no elements to index.

Note that negative is not.

Why: NegativeArraySizeException at run time. Validating the input first, as in Lesson 5b, would prevent it.

Verify: Enter 5 and confirm the length is 5; enter 0 and confirm it is 0 with no error.

Why: Then enter -1 and read the exception. The fix is the guard clause from Lesson 5b — check the value before using it, because the compiler cannot know what the user will type.

9. What are the elements after this?

Prediction

new initialises them for you.

int[] a = new int[3];
indexvalue
00
10
20

Predict first

What does the array contain?

  • Three zeros
  • Three elements holding whatever was in memory
  • Nothing — it is empty
  • Three nulls

Correct: Three zeros

Why: The new operator allocates memory for the array and automatically initialises all its elements to zero. Java never leaves you reading whatever happened to be in memory, which is a guarantee some older languages do not make. For an array of objects the initial value would be null instead.

10. Every element starts at zero

Concept

new initialises the elements for you. What 'zero' means depends on the type, and it is worth knowing the whole set now.

array typeelements start as
int[], long[]0
double[]0.0
char[]the character with code point 0
boolean[]false
String[] and any object arraynull — the subject of Chapter 9

The first four are convenient: a counter array is ready to count the moment it is created, which is exactly what the histogram in the next lesson relies on. The last row is different in kind, and it is where Chapter 9's null keyword becomes important.

11. Declaring an array without creating one

Trap

The trap

The variable exists; the array does not.

int[] counts;
counts[0] = 7;         // no array to put it in
statementwhat existsoutcome
int[] counts;a variable that could refer to an arrayno array yet
counts[0] = 7;still no arraycompile error: variable counts might not have been initialized

This is Lesson 2a's uninitialised-variable error in a new setting. Java catches it at compile time, which is fortunate — the alternative would be writing into memory that was never allocated.

The fix

Declare and create, ideally in one statement.

int[] counts = new int[4];
counts[0] = 7;
statementwhat exists
int[] counts = new int[4];a variable AND a four-element array it refers to
counts[0] = 7;element 0 becomes 7

The distinction between the variable and the array is not pedantry — it is the entire subject of Section 7.4, and getting it clear now makes aliasing straightforward instead of mysterious.

12. Legal array creation?

Definition probe

The size must be a non-negative int expression.

Sort into buckets

Sort each attempt.

creates an array
new int[4]; new int[size], where size was read from the keyboard; new int[0]; {1, 2, 3} as a declaration's initializer
fails
new int[-4]
ok
The size is a non-negative int — a literal, a variable, or implied by the list of values. Zero is a legal size and produces an array with no elements.
no
A negative size throws NegativeArraySizeException at run time. It compiles, because the compiler cannot always know what the expression will evaluate to.

13. Create an array of ten doubles

Fill the middle

Declare and create in one statement.

Fill in the blanks

double[] values = new double[10];

Why: The type is double[] — the element type followed by square brackets — and new allocates the memory and initialises all ten elements to 0.0. Without new you would have declared a variable that refers to no array at all.

14. Why must all the elements have the same type?

Socratic

It is a restriction. Ask what it buys.

Discussion prompt

An array can hold ints or doubles or Strings, but never a mixture. What does that restriction let the compiler and the machine do that a mixed collection would not?

Hint: Think about how the machine finds element number 500.

Answer:

Because every element is the same size, the machine can compute the location of element i by arithmetic — start of the array, plus i times the element size. That is why a[500] is as fast as a[0].

It also lets the compiler check your code: it knows a[i] is an int, so it can reject String s = a[i]; before the program runs. A mixed collection could not offer either guarantee.

This is the same bargain Java makes everywhere — say what type you mean, and get checking and speed in return. Java does have collections that hold mixed types, and they pay for it in both.

15. Accessing elements

Section

Section 7.2

16. The bracket operator selects an element

Concept

The [] operator selects elements from an array, and you can use it anywhere in an expression — on the right of an assignment to read, or on the left to write.

counts[0] = 7;
counts[1] = counts[0] * 2;
counts[2]++;
counts[3] -= 60;
System.out.println("The zeroth element is " + counts[0]);
statementelement changednew value
counts[0] = 7;07
counts[1] = counts[0] * 2;114
counts[2]++;21
counts[3] -= 60;3-60

Every operator you know works on an element: assignment, arithmetic, ++, -=. An element behaves exactly like an ordinary variable of that type — because that is essentially what it is.

17. The array after those four statements

Picture it

The bold numbers inside the boxes are the elements; the lighter numbers outside are the indexes used to identify each location.

Figure (svg): An int array holding 7, 14, 1 and -60 with indexes 0 to 3 marked underneath

As with strings, the index of the first element is 0, not 1 — which is why the first element is sometimes called the zeroth element. For this array the only legal indexes are 0, 1, 2 and 3.

18. Indexing with a loop variable

Worked example

One of the most common ways to index an array is with a loop variable. In this context the name i is short for index.

int i = 0;
while (i < 4) {
    System.out.println(counts[i]);
    i++;
}

// usually written as a for loop:
for (int i = 0; i < 4; i++) {
    System.out.println(counts[i]);
}
iconditioncounts[i]
0true7
1true14
2true1
3true-60
4false — loop ends—

Start the index at 0.

Why: The first element is the zeroth.

Loop while the index is less than the length.

Why: The body runs only when i is 0, 1, 2 or 3 — exactly the legal indexes.

Use i as the index each pass.

Why: counts[i] displays the ith element.

Prefer the for form.

Why: It puts the initialisation, condition and update in one place, exactly as in Lesson 6a.

Verify: Expect 7, 14, 1, -60 on separate lines.

Why: This is the same i < n pattern as the string loop in Lesson 6b, for the same reason: n elements have indexes 0 to n-1, so a strict less-than visits each exactly once.

19. What is counts[1] after these statements?

Prediction

Elements behave like ordinary variables.

int[] counts = new int[4];
counts[0] = 5;
counts[1] = counts[0] * 3;
counts[1]++;
statementcounts[1]
new int[4]0
counts[1] = counts[0] * 315
counts[1]++16

Predict first

What is counts[1]?

  • 16
  • 15
  • 6
  • 0

Correct: 16

Why: counts[0] is 5, so counts[0] * 3 is 15, and the increment then makes it 16. An array element supports every operator an ordinary variable does — assignment, arithmetic, ++ — because it is simply a named storage location reached by index rather than by name.

20. What the variable actually holds

Concept

This is the most important sentence in the chapter: the value of counts is a reference to the array. You should think of the array and the variable that refers to it as two different things.

Figure (svg): A variable box labelled counts with an arrow pointing to a separate four-element array object

The arrow is the point. The variable is a box holding a reference; the array is somewhere else entirely. As you will see in Section 7.4, another variable can be made to refer to the same array, and counts can be made to refer to a different one.

21. Indexing past the end

Trap

The trap

Any index outside 0 to length-1 throws.

int[] counts = new int[4];
counts[4] = 1;      // there is no element 4
counts[-1] = 1;     // and no element -1
index usedlegal rangeoutcome
40 to 3ArrayIndexOutOfBoundsException
-10 to 3ArrayIndexOutOfBoundsException
30 to 3fine — the last element

Both compile. The compiler cannot know what value an index expression will have at run time, so this is a run-time error — and the exception message names the offending index, which usually identifies the bug immediately.

The fix

Legal indexes are 0 to length - 1, and the loop condition is what keeps you inside them.

for (int i = 0; i < counts.length; i++) {
    counts[i] = 0;          // always in range
}
for an array of length nvalue
first index0
last indexn - 1
number of elementsn
loop conditioni < n

Exactly the same table as the string ruler in Lesson 6b, with length in place of length(). One idea, two places — which is why getting it right once is worth the effort.

22. Which index throws?

Prediction

An array of length 4.

int[] a = new int[4];
// which of these throws?
a[0]  a[3]  a[4]  a[-1]
indexlegal?
0yes — the first element
3yes — the last element
4no — one past the end
-1no — indexes are never negative

Predict first

Which indexes throw ArrayIndexOutOfBoundsException?

  • a[4] and a[-1]
  • only a[4]
  • only a[-1]
  • a[3], a[4] and a[-1]

Correct: a[4] and a[-1]

Why: For an array of length 4 the legal indexes are 0, 1, 2 and 3, so both 4 and -1 are out of range. Note that a[3] is fine — it is the last element, at index length minus one, which is the boundary people most often get wrong in the other direction.

23. Print every element

Fill the middle

The standard traversal loop.

Fill in the blanks

for (int i = 0; i < counts.length; i++) ___

Why: i < counts.length visits indexes 0 through length-1, which is every element exactly once. Note that length for an array has no parentheses — unlike String.length(), it is a constant rather than a method, which is one of Java's genuinely confusing inconsistencies.

24. Why is the first element the zeroth?

Explain it to yourself

It causes endless off-by-one errors. Ask what it buys.

Discussion prompt

Indexes start at 0 rather than 1, which is the source of most array bugs. What does zero-based indexing make simpler? Think about how the machine locates element i.

Hint: Where is element 0, relative to the start of the array?

Answer:

Element i lives at start + i times the element size. With zero-based indexing element 0 is at the start with no adjustment; with one-based indexing every access would need a subtraction.

It also makes the loop condition clean: i < length visits exactly length elements, and the same pattern works for strings, arrays and everything else. One-based indexing would need i <= length and would break the correspondence with the count.

The cost is real — the last index is length-1, and that catches everyone. The compensation is that every index expression is one subtraction simpler, in a language where indexing happens constantly.

25. Displaying arrays

Section

Section 7.3

26. println shows you an address, not the elements

Concept

You can pass an array to println, but it probably does not do what you would like.

int[] a = {1, 2, 3, 4};
System.out.println(a);
// output is something like:  [I@bf3f7e0
part of the outputmeaning
[the value is an array
Iof integers
@separator
bf3f7e0the address of the array in memory, in hexadecimal

This is the same shape as printing System.out in Lesson 3a, and for the same reason: what you are printing is a reference, so what you see is where the thing lives rather than what it contains.

27. The variable holds a reference — so that is what prints

Picture it

Once you have the picture from the previous idea, the strange output stops being strange. println is printing the box, not the array.

Figure (svg): A variable a holding a reference arrow to a four-element array, with a note that println prints the reference

To display the elements you have to visit them yourself — or use a library method that does. Both are worth knowing, and the book shows them in that order deliberately.

28. Writing printArray

Worked example

If we want to display the elements, we can do it ourselves. Note the small detail that makes the commas come out right.

public static void printArray(int[] a) {
    System.out.print("{" + a[0]);
    for (int i = 1; i < a.length; i++) {
        System.out.print(", " + a[i]);
    }
    System.out.println("}");
}
stepoutput so far
print("{" + a[0]){1
i = 1: print(", " + a[1]){1, 2
i = 2{1, 2, 3
i = 3{1, 2, 3, 4
println("}"){1, 2, 3, 4}

Print the opening brace and the first element together.

Why: Element 0 is handled outside the loop, which is why the loop starts at 1.

Loop from 1, printing a comma and space before each element.

Why: The separator goes BEFORE each element from the second onwards, so there is never a trailing comma.

Use a.length rather than a literal.

Why: The method then works for arrays of any size.

Close with the brace and a newline.

Why: println rather than print, so the line is finished.

Verify: For {1, 2, 3, 4} expect exactly {1, 2, 3, 4} with no trailing comma.

Why: Then think about what happens for an empty array: a[0] throws, because there is no element 0. Handling that would need a guard — a good illustration that a method which works on your test data is not necessarily correct.

29. What does println(a) display?

Prediction

a is an int array of four elements.

int[] a = {1, 2, 3, 4};
System.out.println(a);
what is passedwhat println shows
the array variablethe value it holds — a reference
the referencetype and address, like [I@bf3f7e0

Predict first

What appears?

  • Something like [I@bf3f7e0 — the type and address
  • [1, 2, 3, 4]
  • {1, 2, 3, 4}
  • 1234

Correct: Something like [I@bf3f7e0 — the type and address

Why: The variable holds a reference to the array, so that is what println displays: [ for array, I for int, and the memory address in hexadecimal. The address differs on each run, exactly as it did when you printed System.out in Lesson 3a — and for the same reason.

30. Arrays.toString does it for you

Concept

The Java library includes a class, java.util.Arrays, providing methods for working with arrays. One of them returns a string representation.

import java.util.Arrays;

int[] a = {1, 2, 3, 4};
System.out.println(Arrays.toString(a));
// [1, 2, 3, 4]
approachoutputeffort
println(a)[I@bf3f7e0none, and useless
your own printArray{1, 2, 3, 4}a method to write and maintain
Arrays.toString(a)[1, 2, 3, 4]one line, and an import

Notice that Arrays.toString uses square brackets instead of curly braces. But as Think Java says, it beats writing your own printArray method — and it handles the empty array correctly, which the hand-written version above does not.

31. Concatenating an array into a message

Trap

The trap

The same problem, harder to spot — because the message around it looks fine.

int[] a = {1, 2, 3};
System.out.println("The array is " + a);
// The array is [I@bf3f7e0
parthow it prints
"The array is "as written
aconverted to a string — which for an array is its address

Concatenation converts the array to a String, and an array's string form is its type and address. The surrounding text makes the output look deliberate, which is why this version survives longer than a bare println.

The fix

Convert the array explicitly with Arrays.toString.

import java.util.Arrays;

int[] a = {1, 2, 3};
System.out.println("The array is " + Arrays.toString(a));
// The array is [1, 2, 3]
writtenoutput
"..." + athe address
"..." + Arrays.toString(a)the elements

A useful habit: if output contains an @ followed by hexadecimal, you printed a reference. That one recognition covers arrays, and every object you meet from Chapter 9 onwards.

32. Match the approach to its output

Matching

Three ways to display the array {1, 2, 3}.

Match the pairs

  • a. System.out.println(a)
  • b. printArray(a), the hand-written method
  • c. System.out.println(Arrays.toString(a))
  • r1. [I@bf3f7e0
  • r2. {1, 2, 3}
  • r3. [1, 2, 3]

Why: println shows the reference; the hand-written method uses curly braces because the book prefers them; and Arrays.toString uses square brackets. The difference between the last two is purely cosmetic — the real difference is that one of them is already written and tested.

33. Why does printArray start its loop at 1?

Error analysis

The loop begins at index 1, not 0, which looks like an off-by-one and is not.

Annotate

  • Element 0 is printed before the loop, together with the opening brace, so the loop's job is only the remaining elements.
  • The separator goes before each element, not after. That is what avoids a trailing comma — {1, 2, 3, 4,} would be the result of putting it after.
  • So starting at 1 is correct here, precisely because element 0 was already handled. It is a deliberate exception to the usual i = 0 pattern, and the comment-free code does not say so.
  • But it breaks on an empty array. a[0] throws ArrayIndexOutOfBoundsException when there is no element 0, and nothing in the method guards against it.
  • Arrays.toString handles that case, which is a small illustration of why library methods are worth preferring: they have been used on inputs you have not thought of.

Reading a method and asking what input would break this? is a habit worth building. The empty array is the classic answer for anything that touches element 0.

34. What else prints as an @ address?

Real world

You have now seen this output shape twice.

Discussion prompt

Printing System.out in Lesson 3a gave java.io.PrintStream@685d72cd, and printing an array gives [I@bf3f7e0. What do those two have in common, and what does it predict about printing other things later in the book?

Hint: What kind of value is each variable holding?

Answer:

Both variables hold references to objects, and Java's default way of turning an object into text is its type followed by @ and its address.

It predicts that every object you create from Chapter 10 onwards will print this way by default — a Point, a Time, a Card. Chapter 11 shows how to fix that by writing a toString method, which is exactly what Arrays.toString is doing for arrays.

The recognition to keep: an @ and some hexadecimal means you printed the reference, not the contents. That single observation will save you a puzzled minute many times over.

35. Copying arrays, and aliasing

Section

Section 7.4

36. Assigning an array variable copies the reference

Concept

Array variables contain references to arrays. When you make an assignment to an array variable, it simply copies the reference — it does not copy the array itself.

double[] a = new double[3];
double[] b = a;

a[0] = 17.0;
System.out.println(b[0]);     // 17.0
statementarrays in existencewhat a and b refer to
new double[3]onea refers to it
double[] b = a;still oneboth a and b refer to it
a[0] = 17.0;onethe change is visible through both
println(b[0])—17.0

alias — One of two or more variables that refer to the same object. Changes made through one are visible through the others.

Any change made through either variable will be seen by the other. Because a and b are different names for the same thing, they are called aliases.

37. Two variables, one array

Picture it

This is the picture the rest of the book depends on. Two boxes, two arrows, one array.

Figure (svg): Two variables a and b each with an arrow pointing to the same three-element array

Compare this against Lesson 2a's int a = 5; int b = a;, where changing a left b alone. The difference is entirely that an array variable holds a reference rather than a value — and that difference is the whole subject of Chapters 9 and 10.

38. Actually copying an array

Worked example

If you want to copy the array rather than the reference, you have to create a new array and copy the elements across.

// by hand:
double[] b = new double[a.length];
for (int i = 0; i < a.length; i++) {
    b[i] = a[i];
}

// or with the library:
double[] b = Arrays.copyOf(a, a.length);
afterarrays in existencea[0] = 17.0 changes
double[] b = a;1both a[0] and b[0]
the copy loop2only a[0]
Arrays.copyOf2only a[0]

Create a second array of the right size.

Why: new double[a.length] — using the length rather than a literal so it works for any array.

Copy the elements one at a time.

Why: The loop reads from a and writes to b; both are ordinary element assignments.

Or use the library.

Why: Arrays.copyOf(a, a.length) does exactly this. Its second parameter is the number of elements to copy, so it can also copy part of an array.

Confirm they are now independent.

Why: Changing a[0] leaves b[0] alone, because they are different arrays.

Verify: Set a[0] = 17.0 after copying and check that b[0] is still 0.0.

Why: That single check is what distinguishes a copy from an alias, and it is worth running deliberately the first time — the two situations look identical in the source and behave completely differently.

39. What does this print?

Prediction

Assignment between array variables.

int[] a = {1, 2, 3};
int[] b = a;
b[0] = 99;
System.out.println(a[0]);
steparraysa[0]
int[] b = a;one, with two names1
b[0] = 99;one99 — the same array

Predict first

What is displayed?

  • 99
  • 1
  • 0
  • a compile error

Correct: 99

Why: int[] b = a; copies the reference rather than the array, so a and b are aliases for the same array and a change through either is visible through the other. Compare this with int x = 5; int y = x; from Lesson 2a, where changing y left x alone — the difference is entirely that an array variable holds a reference.

40. a.length is a constant, not a method

Concept

All arrays have a built-in constant, length, storing the number of elements. This is a genuine inconsistency in Java, and it catches everyone.

Figure (svg): A rule card contrasting the array length constant with the String length method

writtenforcorrect?
a.lengthan arrayyes
a.length()an arrayno — cannot find symbol
s.length()a Stringyes
s.lengtha Stringno

The expression a.length may look like a method invocation, but there are no parentheses and no arguments. The last time a traversal loop executes, i is a.length - 1, which is the index of the last element — and when i equals a.length the condition fails, which is a good thing, because a[a.length] would throw.

41. Copying a reference when you meant to copy the array

Trap

The trap

One array, two names. The 'backup' changes along with the original.

double[] original = {1.0, 2.0, 3.0};
double[] backup = original;      // NOT a copy

original[0] = 99.0;
System.out.println(backup[0]);   // 99.0 - the backup changed too
steparraysbackup[0]
double[] backup = original;11.0
original[0] = 99.0;199.0 — through the alias

There was never a backup. The assignment copied the reference, so both names point at the one array — and the original values are gone for good.

The fix

Create a second array.

double[] original = {1.0, 2.0, 3.0};
double[] backup = Arrays.copyOf(original, original.length);

original[0] = 99.0;
System.out.println(backup[0]);   // 1.0 - genuinely unaffected
steparraysbackup[0]
Arrays.copyOf(...)21.0
original[0] = 99.0;21.0 — a different array

The test that tells them apart: change one and look at the other. If both changed, you have an alias; if only one did, you have a copy. Everything else about the two situations looks the same in the source.

42. Alias or copy?

Definition probe

Ask how many arrays exist afterwards.

Sort into buckets

Sort each pair of statements.

an alias — one array
int[] b = a;
a copy — two arrays
int[] b = Arrays.copyOf(a, a.length);; int[] b = new int[a.length]; then a copy loop; int[] b = a; then b = new int[3];
alias
The assignment copies only the reference, so both variables name the same array and changes through one are visible through the other.
copy
A second array is created — by copyOf, by new, or both — so the two variables refer to different arrays and changes to one do not affect the other.

43. Watch the references

Invariant

Step through an aliasing example and watch how many arrays exist.

Step through it

At which frame does the number of arrays change from one to two?

  1. One array exists, and a refers to it. All three elements are 0.0.
  2. Still ONE array. b now refers to it as well — two arrows, one target.
  3. The change is made through a and is visible through b, because there is only one array to change.
  4. Now a second array exists, holding a copy. b's arrow has moved to it; a still points at the first.
  5. This change affects only a's array. b's copy was taken before it and is unaffected.

The fourth. Until copyOf runs there is exactly one array however many variables name it — and counting arrays rather than variables is the habit that makes aliasing predictable.

44. What does b = new int[3] do to a?

Edge cases

Reassigning a variable is different from changing an element.

Discussion prompt

After int[] b = a;, what is the difference between writing b[0] = 99; and writing b = new int[3];? Which one affects a, and why?

Hint: One changes the array; the other changes which array b points at.

Answer:

b[0] = 99; changes the array that both variables refer to, so a sees it. b = new int[3]; changes what b refers to, leaving a pointing at the original array, which is unaffected.

So the arrow and the boxes are separate things you can change independently. Assigning to b moves the arrow; assigning to b[i] changes a box the arrow points at.

This distinction is exactly what Chapter 10 calls mutability, and it is why the book insists you think of the array and the variable that refers to it as two different things. Getting it now makes three later chapters straightforward.

45. Traversing arrays

Section

Section 7.5

46. Looping through the elements is called a traversal

Concept

Many computations can be implemented by looping through the elements of an array and performing an operation on each. This is a traversal, and the simplest kind transforms every element in place.

int[] a = {1, 2, 3, 4, 5};
for (int i = 0; i < a.length; i++) {
    a[i] *= a[i];
}
// a is now {1, 4, 9, 16, 25}
ia[i] beforea[i] after
011
124
239
3416
4525

Note that this modifies the array in place rather than building a new one — which works because a[i] on the left of an assignment writes into the array the variable refers to.

47. The array before and after

Picture it

Each element replaced by its own square, in one pass.

Figure (svg): An array of five elements holding 1, 4, 9, 16 and 25 after each was squared in place

This is the first of three traversal shapes. The other two do not change the array at all — one looks for something in it, and one reduces it to a single value.

48. Search: traverse and return an index

Worked example

A search traverses an array looking for a particular element, and returns where it found it. This code is essentially what String.indexOf does.

public static int search(double[] array, double target) {
    for (int i = 0; i < array.length; i++) {
        if (array[i] == target) {
            return i;
        }
    }
    return -1;        // not found
}
iarray[i]equals 1.23?action
03.14nocontinue
1-55.0nocontinue
21.23yesreturn 2
———the last line is never reached

Traverse the array with an index loop.

Why: You need the index, because the index is what you are going to return.

Return immediately on a match.

Why: There is no reason to keep looking, and returning from inside the loop is the clearest way to say so.

Return -1 if the loop finishes.

Why: Reaching the line after the loop means every element was checked and none matched. -1 is a special value chosen to indicate a failed search.

Note why -1 is safe.

Why: Indexes are never negative, so it cannot be confused with a real answer — exactly as in Lesson 6b's indexOf.

Verify: For {3.14, -55.0, 1.23, -0.8} searching for 1.23, expect 2 — because array indexes start at 0.

Why: Then search for a value that is not present and confirm you get -1. And note the caution from Lesson 2b: comparing doubles with == is unreliable in general, which makes this method a better illustration than an implementation.

49. Which traversal pattern is it?

Definition probe

Three shapes: transform every element, search for one, reduce to a single value.

Sort into buckets

Sort each task.

transform
double every element; replace every element with its absolute value
search
find the index of the first negative number
reduce
add up all the elements; find the largest element
t
Every element is changed in place, and the array has the same length afterwards.
s
The array is unchanged; the result is a position — or -1 if nothing matched.
r
The array is unchanged; many values are combined into one, using an accumulator initialised before the loop.

50. Reduce: many values down to one

Concept

Another common traversal is a reduce operation, which reduces an array of values to a single value — a sum, a product, a minimum, a maximum.

public static double sum(double[] array) {
    double total = 0.0;
    for (int i = 0; i < array.length; i++) {
        total += array[i];
    }
    return total;
}
iarray[i]total after
before—0.0
03.03.0
14.07.0
25.012.0

traversal — Looping through the elements of an array.

accumulator — A variable used to accumulate a running total or other combined result during a traversal.

Before the loop, total is initialised to 0. Each pass updates it by adding one element, and at the end it contains the sum. A variable used this way is called an accumulator, because it accumulates the running total.

51. Declaring the accumulator inside the loop

Trap

The trap

The accumulator is reset every pass, so it only ever holds the last element.

public static double sum(double[] array) {
    for (int i = 0; i < array.length; i++) {
        double total = 0.0;      // inside the loop
        total += array[i];
    }
    return total;                // and out of scope here
}
itotal at the start of the passtotal at the end
00.0 — freshly declared3.0
10.0 — declared AGAIN4.0
20.05.0

Two things are wrong at once: the running total is destroyed on every pass, and total is out of scope at the return — which is Lesson 6a's point that a variable declared inside a block lives only in that block. The compile error actually saves you here.

The fix

Declare the accumulator before the loop, so it survives across passes.

public static double sum(double[] array) {
    double total = 0.0;
    for (int i = 0; i < array.length; i++) {
        total += array[i];
    }
    return total;
}
where total is declaredsurvives the pass?in scope at the return?
before the loopyesyes
inside the loopno — recreated each passno

The rule generalises: anything that has to remember something across passes must be declared outside the loop. Accumulators, counters, best-so-far values — all of them.

52. What does search return?

Prediction

The target is not in the array.

double[] array = {3.14, -55.0, 1.23};
int index = search(array, 9.9);
imatch?
0no
1no
2no
loop endsthe line after it runs

Predict first

What is index?

  • -1
  • 0
  • 3
  • an exception is thrown

Correct: -1

Why: Every element is checked, none matches, and the loop finishes — so execution reaches the return statement after it, which returns -1. Because indexes are never negative, -1 can safely mean 'not found', and the caller must check for it before using the result as an index.

53. Write a reduce

Fill the middle

Sum the elements of an int array.

Fill in the blanks

int total = 0;
for (int i = 0; i < a.length; i++) +=} a[i];
}

Why: The accumulator is initialised to 0 before the loop, so it survives across passes, and += adds each element to the running total. Declaring total inside the loop would reset it every pass and leave it out of scope afterwards.

54. What should sum return for an empty array?

Counterexample

Test the reduce pattern at its edge.

Discussion prompt

sum initialises total to 0.0 and adds every element. What does it return for an array with no elements — and is that the right answer? Now ask the same question for a method that finds the maximum.

Hint: The two have different answers, and that is the interesting part.

Answer:

sum returns 0.0, and that is exactly right: the sum of no numbers is zero, and the loop simply runs zero times.

Maximum has no such answer. There is no sensible maximum of no numbers, and the usual trick of initialising to zero is wrong — for an array of negative numbers it would return 0, which is not in the array at all.

So a reduce needs a starting value that is neutral for its operation — 0 for sum, 1 for product — and when no such value exists, the empty case has to be handled separately. This is the kind of edge case that separates a method that works on your test data from one that is correct.

55. Arrays and Strings, side by side

Comparison

They share an indexing scheme and differ in two specific ways. Fill the blanks.

Comparison matrix

Stringarray
first index00
last indexlength() - 1length - 1
how you get the sizes.length() — a methoda.length — a constant, no parentheses
can you change an element?no — Strings are immutable (Chapter 9)yes — a[i] = x
out-of-range accessStringIndexOutOfBoundsExceptionArrayIndexOutOfBoundsException

The third row is the inconsistency that catches everyone, and the fourth is the one that matters most: an array can be modified through any reference to it, which is what makes aliasing consequential.

56. The pattern to carry away

Pattern

Three traversal shapes cover almost everything you will do with an array, and they differ in what they keep outside the loop.

// transform: change every element in place
for (int i = 0; i < a.length; i++) {
    a[i] = f(a[i]);
}

// search: return the index, or -1
for (int i = 0; i < a.length; i++) {
    if (a[i] == target) return i;
}
return -1;

// reduce: accumulate into one value
double total = 0.0;                  // OUTSIDE the loop
for (int i = 0; i < a.length; i++) {
    total += a[i];
}
return total;
patternwhat lives outside the loopwhat it returns
transformnothingnothing — the array is changed
searchnothingan index, or -1
reducethe accumulatorthe accumulated value

57. Check: aliasing

Check

Work it out before you click.

int[] a = {1, 2, 3};
int[] b = a;
int[] c = Arrays.copyOf(a, a.length);
a[0] = 99;
variablerefers toelement 0 after
athe original array99
bthe same array99
ca separate copy1

Check your understanding

What are b[0] and c[0]?

  • A. b[0] is 99 and c[0] is 1 (correct)
  • B. both are 99
  • C. both are 1
  • D. b[0] is 1 and c[0] is 99

Answer: A

Why: b = a copies the reference, so b is an alias for the same array and sees the change. Arrays.copyOf creates a second array, so c holds an independent copy taken before the change and is unaffected.

Why B tempts people
This assumes copyOf also produces an alias, but it creates a genuinely separate array.
Why C tempts people
This assumes plain assignment copies the array, which is exactly the misconception this section exists to correct.
Why D tempts people
This reverses the two: it is the plain assignment that aliases, and copyOf that copies.

58. Check: length

Check

Work it out before you click.

int[] a = new int[5];
String s = "hello";
expressionlegal?
a.lengthyes — a constant
a.length()no
s.length()yes — a method
s.lengthno

Check your understanding

Which pair of expressions is correct?

  • A. a.length and s.length() (correct)
  • B. a.length() and s.length()
  • C. a.length and s.length
  • D. a.length() and s.length

Answer: A

Why: An array's length is a built-in constant with no parentheses, while a String's length is a method that requires them. There is no logic to the inconsistency — it simply has to be memorised, and it is one of the most common compile errors beginners hit.

Why B tempts people
a.length() treats the array's constant as a method, which does not compile.
Why C tempts people
s.length treats the String's method as a field, which does not compile.
Why D tempts people
Both halves are wrong — this is the correct answer with each half swapped.

59. Check: the traversal loop

Check

Work it out before you click.

int[] a = {10, 20, 30};
int total = 0;
for (int i = 0; i < a.length; i++) {
    total += a[i];
}
ia[i]total after
01010
12030
23060

Check your understanding

What is total after the loop?

  • A. 60 (correct)
  • B. 30
  • C. 3
  • D. 0

Answer: A

Why: The accumulator starts at 0 and each pass adds one element, so it ends holding the sum of all three: 60. Declaring total before the loop is what allows it to survive across passes — inside the loop it would be reset each time and out of scope afterwards.

Why B tempts people
This is the value after two passes, or the result of the accumulator being reset before the last one.
Why C tempts people
This counts the elements rather than summing them, which would be total++ instead of total += a[i].
Why D tempts people
This would be the result if the loop never ran — but a.length is 3, so it runs three times.

60. Aliasing is not a beginner's trap

Real world

Section 7.4 looks like a small technical point. It is the foundation of three later chapters and a large share of real bugs.

Discussion prompt

You pass an array to a method, and the method changes one of its elements. Does the caller see the change? Reason it out from what parameter passing does and what an array variable holds.

Hint: Lesson 4a said a method gets a copy of the argument. A copy of what, exactly?

Answer:

Yes, the caller sees it. Parameter passing copies the argument's value — and the value of an array variable is a reference. So the method gets its own variable pointing at the same array, and any element it changes is changed for everyone.

That is not a contradiction of Lesson 4a. A method still cannot change which array the caller's variable refers to — assigning a = new int[3] inside the method affects only the method's own copy of the reference. It can change the array's contents, not the caller's arrow.

This is the single most useful thing to take from Chapter 7. It is why Chapter 10 is called Mutable Objects, why Chapter 9 spends a whole chapter on immutable ones, and why a great deal of real debugging comes down to asking who else has a reference to this?

61. How sure are you?

Commit first

Commit to an answer and to your confidence.

Predict first

After int[] a = {1, 2, 3}; int[] b = a; b[0] = 99; what is a[0]?

  • 99
  • 1
  • 0
  • a compile error

Correct: 99

Why: The assignment int[] b = a; copies the reference, not the array, so a and b are two names for the same array — aliases. Changing an element through b changes the one array, and a sees it. Compare this against int x = 5; int y = x; y = 99; from Lesson 2a, where x stays 5: the difference is entirely that a primitive variable holds a value while an array variable holds a reference. If you were confident the answer was 1, that is the most valuable misconception to have corrected before Chapter 9.

62. Explain it to someone else

Explain it

Two minutes, drawing as you go.

Discussion prompt

A classmate made a 'backup' of an array with int[] backup = original; and is confused that editing the original also changed the backup. Draw the memory diagram and explain. Then show them the two ways to make a real copy.

Hint: Draw the boxes and the arrows separately, and count the arrays.

Answer:

Draw two variable boxes and one array. Both boxes have arrows to the same array — that is what the assignment did. It copied the arrow, not the boxes it points at. So there is only one array, and changing it through either name changes it for both.

To make a real copy you need a second array: either new int[original.length] and a loop that copies each element, or Arrays.copyOf(original, original.length), which does the same thing in one line.

If the drawing has two arrays before you get to copyOf, start again — the whole explanation depends on the listener seeing that there was only ever one.

63. Exit ticket

Exit ticket

One question before you close the deck.

Predict first

What does an array variable actually hold?

  • A reference to the array, which lives elsewhere in memory
  • The elements themselves
  • The first element of the array
  • The length of the array

Correct: A reference to the array, which lives elsewhere in memory

Why: You should think of the array and the variable that refers to it as two different things: the variable is a box holding a reference, and the array is somewhere else. That single fact explains everything surprising in this lesson — why printing the variable shows an address, why assigning one array variable to another creates an alias rather than a copy, and why a method can change the contents of an array it was passed. It is also the foundation of Chapters 9, 10 and 11.

64. Draw the whole lesson

Connect it up

One page, from memory.

Draw it

Draw the memory diagram for double[] a = new double[3]; double[] b = a; — two variable boxes and however many arrays actually exist. Then draw it again after b = Arrays.copyOf(a, 3); and mark what changed. Beside them, write the three traversal patterns — transform, search, reduce — and mark which one needs a variable declared outside the loop and why. Finally write a.length and s.length() and circle the difference.

65. Recap

Recap

Five sections that introduce the first data structure — and, more importantly, the first time a variable does not hold its own value.

if you remember one thingit is this
about array variablesthe variable and the array are two different things
about copyingassignment copies the arrow, not the boxes
about traversalthe accumulator goes outside the loop

Sources

  1. Downey & Mayfield, Think Java, 2nd edition (Green Tea Press / O'Reilly, 2020) — Think Java 2e, Chapter 7 (Arrays and References), Sections 7.1-7.5, pp. 107-115
  2. The Java Tutorials — Arrays
  3. Think Java 2e — free online edition and source code

Want this taught 1-on-1? Alexander tutors Java — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108