Arrays
How do we store thirty marks without thirty variables?
By the end of this lecture you will be able to
- Explain an array as a row of numbered boxes under one name
- Declare, create and initialise an array, and know its default values
- Read and write elements using the index and
length - Traverse an array with
forand with enhancedfor, and know when the second will not do - Read an
ArrayIndexOutOfBoundsExceptionmessage and fix the<= lengthbug - Pass an array to a method and return one from it, and explain why a method can change its elements
In this lecture
- Thirty marks in thirty variables is a program nobody wants to write
- An array is a row of numbered boxes under one name
- Three steps: declare, create, then give values
- A new array is filled with default values
- Indexes start at 0, and the last one is
length - 1 - An element is read and written like any variable
- A
forloop walks over the boxes one by one - The same loop reads an array from the keyboard
- An index outside the array stops the program while it runs
<= lengthis the most famous array bug- Enhanced
forreads every element without an index - A method receives a whole array in one parameter
- A method cannot change a primitive argument, but it can change array elements
- A method can create a new array and return it
- Review questions
Thirty marks in thirty variables is a program nobody wants to write
So far a variable holds one value. Five students in a section means five variables. Thirty students means thirty variables, thirty lines of reading and one very long addition.
The tiring way
Run the program, then imagine adding a sixth student: how many lines must change? Try it.
public class FiveMarks {
public static void main(String[] args) {
int mark1 = 85;
int mark2 = 70;
int mark3 = 92;
int mark4 = 60;
int mark5 = 77;
int sum = mark1 + mark2 + mark3 + mark4 + mark5;
System.out.println(sum);
}
}384
An array is a row of numbered boxes under one name
- Each box is an ordinary
intvariable, called an element. - The whole row has one name,
marks, and each box has a number called its index. - All the elements of an array have the same type.
- The number of boxes is set at creation and never changes.
| Variable | Array |
|---|---|
| One box | A row of boxes |
Reached by its name: mark | An element is reached by name and index: marks[2] |
| No index | The index can be a variable: marks[i] |
In the array above, which expression gives the mark 60, the fourth in the row?
Three steps: declare, create, then give values
int[] means an array of ints. This line reserves the name only; there are no boxes yet.
int[] marks;new creates the row with the number of boxes you ask for.
marks = new int[5];Put a value in each box, as with any variable.
marks[0] = 85;The three steps in a program
Press "Debug line by line" and watch the row appear after the new line, then fill up. Add a fourth element: which two lines must change?
public class ThreeSteps {
public static void main(String[] args) {
int[] marks;
marks = new int[3];
marks[0] = 85;
marks[1] = 70;
marks[2] = 92;
System.out.println(marks[0] + marks[1] + marks[2]);
}
}247
If you already know the values, write them at once between curly brackets. All three steps happen in one line and the compiler counts the boxes itself: int[] marks = {85, 70, 92};
Declared but not created
Fix the program so it prints 85.
public class ForgotNew {
public static void main(String[] args) {
int[] marks;
marks[0] = 85;
System.out.println(marks[0]);
}
}might not have been initializedShow the fix
public class ForgotNew {
public static void main(String[] args) {
int[] marks = new int[3];
marks[0] = 85;
System.out.println(marks[0]);
}
}85
Why? Declaring alone creates no boxes, so there is no marks[0] to write to. The message says marks has no value, because it is new that gives it a row.
Curly brackets only on the declaring line
Join the two lines into one so the program compiles.
public class BracesLater {
public static void main(String[] args) {
int[] marks;
marks = {85, 70, 92};
System.out.println(marks[2]);
}
}illegal start of expressionShow the fix
public class BracesLater {
public static void main(String[] args) {
int[] marks = {85, 70, 92};
System.out.println(marks[2]);
}
}92
Why? The bare {...} form is allowed only on the declaring line itself. The message does not say so; it only says the compiler did not expect a bracket here, which is why it looks unrelated to the cause.
A new array is filled with default values
| Element type | Default value |
|---|---|
int | 0 |
double | 0.0 |
boolean | false |
char | The character with code zero, invisible when printed |
String | null |
What is in the boxes before we write to them?
Predict the four lines before running, then add the line counts[0] = 5; before the printing and run again.
public class Defaults {
public static void main(String[] args) {
int[] counts = new int[3];
double[] prices = new double[3];
boolean[] paid = new boolean[3];
String[] names = new String[3];
System.out.println(counts[0]);
System.out.println(prices[1]);
System.out.println(paid[2]);
System.out.println(names[0]);
}
}0 0.0 false null
Indexes start at 0, and the last one is length - 1
Every array keeps its box count in length. In an array of 4 elements the indexes are 0, 1, 2 and 3, so index 4 does not exist.
First, last and length
Add two elements inside the curly brackets and run. Notice the last element still prints correctly without changing the lines below.
public class IndexAndLength {
public static void main(String[] args) {
int[] marks = {85, 70, 92, 60};
System.out.println(marks.length);
System.out.println(marks[0]);
System.out.println(marks[marks.length - 1]);
}
}4 85 60
length has no parentheses
public class LengthParens {
public static void main(String[] args) {
int[] marks = {85, 70, 92};
System.out.println(marks.length());
}
}cannot find symbolShow the fix
public class LengthParens {
public static void main(String[] args) {
int[] marks = {85, 70, 92};
System.out.println(marks.length);
}
}3
Why? An array's length is a stored value, not a method, so the parentheses send the compiler looking for a method of that name, and there is none.
Given int[] a = new int[10];, what is the largest allowed index?
An element is read and written like any variable
marks[1] is a full variable: it goes on the left of = to receive a value, on the right to be read, and it accepts ++ and +=. What goes in the brackets can be any expression that gives an int.
Draw the three boxes on paper and update them after each line. What is printed?
public class ReadWrite {
public static void main(String[] args) {
int[] a = new int[3];
a[0] = 4;
a[1] = a[0] + 5;
a[2] = a[1] * 2;
a[0] = a[2] - a[1];
System.out.println(a[0]);
System.out.println(a[1]);
System.out.println(a[2]);
}
}Show the output
9 9 18
Why? Each line reads what the boxes hold now: a[1] becomes 9, a[2] becomes 18, then a[0] changes from 4 to 18 − 9 = 9.
The index can be a variable or an expression
Predict the four lines, then run. Change i to 0 and predict again. What if i is 3? Try it; we will explain what happens shortly.
public class IndexExpression {
public static void main(String[] args) {
int[] marks = {85, 70, 92, 60};
int i = 1;
System.out.println(marks[i]);
System.out.println(marks[i + 1]);
marks[i] = marks[i] + 10;
marks[i + 1]++;
System.out.println(marks[1]);
System.out.println(marks[2]);
}
}70 92 80 93
A for loop walks over the boxes one by one
The pattern you will write hundreds of times
Press "Debug line by line" and watch i move across the indexes. Then change the loop to print from last to first: start at marks.length - 1 and stop at 0.
public class Traverse {
public static void main(String[] args) {
int[] marks = {85, 70, 92};
for (int i = 0; i < marks.length; i++) {
System.out.println("marks[" + i + "] = " + marks[i]);
}
}
}marks[0] = 85 marks[1] = 70 marks[2] = 92
| Part | Why |
|---|---|
int i = 0 | The first index |
i < marks.length | The last index is one less than the length, and this stays right if the element count changes |
i++ | The next box |
marks[i] | The current element |
The same loop reads an array from the keyboard
We need not know the box count when writing the program: read the count first, create an array of that size, then fill it with a loop.
Read the marks, then print them
The input here is 3, then 85, 70 and 92. Notice the mark number shown to the user is i + 1, because people count from 1. Change the program to print the sum of the marks at the end.
import java.util.Scanner;
public class ReadMarks {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("How many marks? ");
int n = in.nextInt();
int[] marks = new int[n];
for (int i = 0; i < marks.length; i++) {
System.out.print("Mark " + (i + 1) + ": ");
marks[i] = in.nextInt();
}
System.out.println();
System.out.println("You entered:");
for (int i = 0; i < marks.length; i++) {
System.out.println(marks[i]);
}
}
}How many marks? Mark 1: Mark 2: Mark 3: You entered: 85 70 92
Print backwards
Read an int n, then n ints into an array. Print the elements from last to first, one per line. Print no prompts.
import java.util.Scanner;
public class Backwards {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// Read n, create the array, fill it, then print it backwards
}
}3 10 20 30
30 20 10
Hint 1
You need two loops: one to read and one to print.
Hint 2
The printing loop: for (int i = n - 1; i >= 0; i--).
Solution
import java.util.Scanner;
public class Backwards {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] numbers = new int[n];
for (int i = 0; i < n; i++) {
numbers[i] = in.nextInt();
}
for (int i = n - 1; i >= 0; i--) {
System.out.println(numbers[i]);
}
}
}An index outside the array stops the program while it runs
Planned test: we break the program on purpose
The array has five elements, so marks[5] does not exist. Run the program and read the whole red message. Notice the compiler did not object, and Done was not printed. Change 5 to 7 and watch the number in the message change.
public class OutOfBounds {
public static void main(String[] args) {
int[] marks = {85, 70, 92, 60, 77};
System.out.println(marks[4]);
System.out.println(marks[5]);
System.out.println("Done");
}
}77
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at OutOfBounds.main(OutOfBounds.java:5)| Part | Meaning |
|---|---|
ArrayIndexOutOfBoundsException | The exception name: the index is outside the array |
5 | The bad index itself, the most useful part of the message |
OutOfBounds.java:5 | The file and line where the program stopped |
What appears in normal output before the program stops?
public class CrashPoint {
public static void main(String[] args) {
int[] a = {1, 2, 3};
int i = 2;
System.out.println(a[i]);
i++;
System.out.println("i is " + i);
System.out.println(a[i]);
System.out.println("end");
}
}Show the output
3 i is 3
Why? Lines run in order until the program reaches a[3]. It stops there without printing end, and an array does not give 0 for an index that does not exist.
<= length is the most famous array bug
Fix the loop
This program compiles, then stops while running after printing every element. Run it and read the number in the message: what was i? Fix the condition so the word Done appears, and use "Debug line by line" to see the last round.
public class LengthBug {
public static void main(String[] args) {
int[] marks = {85, 70, 92};
for (int i = 0; i <= marks.length; i++) {
System.out.println(marks[i]);
}
System.out.println("Done");
}
}85 70 92
Which loop visits every element of a with no error?
Enhanced for reads every element without an index
"For each mark in marks"
Read the line like this: for each mark in marks. In each round mark holds a copy of the next element. Add an element and print the sum of the elements.
public class EnhancedFor {
public static void main(String[] args) {
int[] marks = {85, 70, 92};
for (int mark : marks) {
System.out.println(mark);
}
}
}85 70 92
Does the first loop change the array's elements?
public class EnhancedCopy {
public static void main(String[] args) {
int[] numbers = {1, 2, 3};
for (int n : numbers) {
n = n * 10;
}
for (int n : numbers) {
System.out.println(n);
}
}
}Show the output
1 2 3
Why? n is a separate variable holding a copy of the element. Changing the copy does not reach the original box. The same idea returns shortly with methods.
| You want to | Enhanced for | Plain for |
|---|---|---|
| Read every element in order | Yes, and it is clearest | Yes |
| Know the element's index | No | Yes |
| Change the array's elements | No | Yes |
| Go from last to first | No | Yes |
| Visit only part of the array | No | Yes |
| Compare an element with the next | No | Yes |
After the colon goes an array, not a number
public class ForEachLength {
public static void main(String[] args) {
int[] marks = {85, 70};
for (int mark : marks.length) {
System.out.println(mark);
}
}
}for-each not applicable to expression typeShow the fix
public class ForEachLength {
public static void main(String[] args) {
int[] marks = {85, 70};
for (int mark : marks) {
System.out.println(mark);
}
}
}85 70
Why? marks.length is a single number, and enhanced for needs something it can walk over element by element.
A method receives a whole array in one parameter
A method that works with any length
The parameter type is int[], and in the call we pass just the name. Call total with a third array of one element.
public class ArrayParameter {
static int total(int[] numbers) {
int sum = 0;
for (int n : numbers) {
sum += n;
}
return sum;
}
public static void main(String[] args) {
int[] section1 = {85, 70, 92};
int[] section2 = {60, 77};
System.out.println(total(section1));
System.out.println(total(section2));
}
}247 137
No square brackets in the call
public class CallBrackets {
static int first(int[] numbers) {
return numbers[0];
}
public static void main(String[] args) {
int[] marks = {85, 70};
System.out.println(first(marks[]));
}
}'.class' expectedShow the fix
public class CallBrackets {
static int first(int[] numbers) {
return numbers[0];
}
public static void main(String[] args) {
int[] marks = {85, 70};
System.out.println(first(marks));
}
}85
Why? int[] belongs in the method definition to state the type; in the call we write just the array name. The message '.class' expected looks very strange, but this is usually why it appears.
A method cannot change a primitive argument, but it can change array elements
In the methods lecture our attempt to write a method that swaps two variables failed: arguments pass by value, so the method works on a copy. Run both examples under the debugger and compare.
Remember: the failed swap
Press "Debug line by line". Inside swap, a and b trade values, but they are copies. Back in main, x and y have not changed.
public class FailedSwap {
static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
public static void main(String[] args) {
int x = 5;
int y = 9;
swap(x, y);
System.out.println(x + " " + y);
}
}5 9
The same swap inside an array works
Debug line by line and stop inside swap. Notice arr and pair point to the same row, so what the method writes, main sees.
public class ArraySwap {
static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void main(String[] args) {
int[] pair = {5, 9};
swap(pair, 0, 1);
System.out.println(pair[0] + " " + pair[1]);
}
}9 5
| What the variable holds | What reaches the method | Is main affected? |
|---|---|---|
int x: the value 5 itself | A copy of 5 | No |
int[] pair: an arrow pointing to the row | A copy of the arrow, pointing to the same row | Yes, in the elements |
Two similar methods, one taking an int, the other an int[]. What does the program print?
public class DoubleBoth {
static void doubleValue(int x) {
x = x * 2;
}
static void doubleAll(int[] arr) {
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] * 2;
}
}
public static void main(String[] args) {
int n = 5;
int[] a = {3, 4};
doubleValue(n);
doubleAll(a);
System.out.println(n);
System.out.println(a[0]);
System.out.println(a[1]);
}
}Show the output
5 6 8
Why? doubleValue changed only its copy of 5. doubleAll wrote into the row that a points to.
The same thing without a method: int[] b = a;. Do we now have two rows?
public class TwoNames {
public static void main(String[] args) {
int[] a = {1, 2, 3};
int[] b = a;
b[0] = 99;
System.out.println(a[0]);
System.out.println(b[0]);
}
}Show the output
99 99
Why? Only new creates a new row. int[] b = a; copied the arrow, so the one row now has two names.
A bonus method
Write the method static void addBonus(int[] marks, int bonus) that raises each mark by bonus inside the same array, never going above 100. In main read n, then n marks, then bonus. Call the method and print the marks, one per line.
import java.util.Scanner;
public class Bonus {
// Write addBonus here
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// Read the input, call addBonus, print the marks
}
}3 50 90 70 5
55 95 75
Hint 1
To change elements you need plain for; the enhanced one works on a copy.
Hint 2
The method returns nothing: main sees the change in the same array.
Solution
import java.util.Scanner;
public class Bonus {
static void addBonus(int[] marks, int bonus) {
for (int i = 0; i < marks.length; i++) {
marks[i] = marks[i] + bonus;
if (marks[i] > 100) {
marks[i] = 100;
}
}
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] marks = new int[n];
for (int i = 0; i < n; i++) {
marks[i] = in.nextInt();
}
int bonus = in.nextInt();
addBonus(marks, bonus);
for (int mark : marks) {
System.out.println(mark);
}
}
}A method can create a new array and return it
A method whose return type is int[]
Press "Debug line by line". result ends when the method ends, but the row survives because main took the arrow to it. Change the method to return cubes instead of squares.
public class ReturnArray {
static int[] squares(int n) {
int[] result = new int[n];
for (int i = 0; i < n; i++) {
result[i] = (i + 1) * (i + 1);
}
return result;
}
public static void main(String[] args) {
int[] s = squares(4);
for (int value : s) {
System.out.println(value);
}
}
}1 4 9 16
The first even numbers
Write the method static int[] firstEvens(int n) that returns an array holding the first n even numbers: 2, 4, 6 and so on. In main read n and print the elements, one per line.
import java.util.Scanner;
public class FirstEvens {
// Write firstEvens here
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// Read n, call firstEvens, print the result
}
}4
2 4 6 8
Hint 1
The element at index i equals (i + 1) * 2.
Solution
import java.util.Scanner;
public class FirstEvens {
static int[] firstEvens(int n) {
int[] evens = new int[n];
for (int i = 0; i < n; i++) {
evens[i] = (i + 1) * 2;
}
return evens;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
for (int value : firstEvens(n)) {
System.out.println(value);
}
}
}From mark to percentage
Write the method static double[] toPercent(int[] marks, int outOf) that returns a new array holding each mark as a percentage, leaving the original unchanged. In main read n, then outOf, then n marks. Print each percentage on its own line, then print the first original mark.
import java.util.Scanner;
public class Percent {
// Write toPercent here
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// Read the input, call toPercent, print the results
}
}2 50 45 25
90.0 50.0 45
Hint 1
The new array has length marks.length and type double[].
Hint 2
Remember integer division: write marks[i] * 100.0 / outOf.
Solution
import java.util.Scanner;
public class Percent {
static double[] toPercent(int[] marks, int outOf) {
double[] result = new double[marks.length];
for (int i = 0; i < marks.length; i++) {
result[i] = marks[i] * 100.0 / outOf;
}
return result;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int outOf = in.nextInt();
int[] marks = new int[n];
for (int i = 0; i < n; i++) {
marks[i] = in.nextInt();
}
for (double p : toPercent(marks, outOf)) {
System.out.println(p);
}
System.out.println(marks[0]);
}
}Review questions
What does new double[4] hold right after it is created?
double it is 0.0.Which task can enhanced for not do?
for holds a copy of each element, so writing to it does not reach the box. The others only read.A program stops with ArrayIndexOutOfBoundsException: 3. What does 3 mean?
<= length bug, but it can be any value out of range. The line number appears on the next line of the message.This method creates a new row and writes to it. What does the program print?
public class Replace {
static void replace(int[] arr) {
arr = new int[2];
arr[0] = 9;
}
public static void main(String[] args) {
int[] a = {1, 2};
replace(a);
System.out.println(a[0]);
}
}Show the output
1
Why? arr is a copy of the arrow. The first line made that copy point to a new row, so a still points to the old one. A method can change the elements of a row it reaches, but not the arrow held in main.