Module 5, Part 1: Arrays#

Up until now, we have seen how to write programs that handle values of various types — e.g., int, boolean, String… Moreover, all the programs we have seen until now have a common characteristic: we exactly know how many values each program maintains in memory at each point during its execution. This can be determined by selecting a position in the program code, and looking at which variables are in scope.

However, a program may sometimes need to handle a number of values that is only known at run-time. In this case, we can use arrays. An array is a structured data type: when a value of type “array” is created, it acts as a container for other values, which we can access and modify.

Arrays#

An array is a collection of values of the same type; each value contained in an array is identified by an index, which is the position of the value inside the array itself. Most programming languages offer arrays as a fundamental data stucture, i.e. a way to organise, store, and access values in the computer memory.

Before delving into arrays, we first consider a problem that requires us to use arrays. Then, we explore:

We also discuss a very useful example: how to split a string into an array of strings.

Goal: an Interactive Program that Reads Lotto Numbers#

Our goal is to write a program that runs on the console (a.k.a. terminal) and behaves as follows:

  1. Asks the user how many Lotto numbers they want to play;

  2. Reads the Lotto numbers one by one, ensuring that:

    • each number is between 1 and 36, and

    • each number is unique.

    If one of the conditions above does not hold, the program asks the user to enter a correct Lotto number instead.

  3. Finally, the program prints a summary of all the Lotto numbers provided by the user.

Based on what we have seen so far, the description above means that we need to write a program that uses some form of loop. However, we have not yet seen how a Java program can store and read an arbitrary number of values (in this case, determined by how many Lotto numbers the user wishes to play). To achieve this, we will use an array to store the lotto numbers.

Example 34 (Lotto numbers)

The following Java program behaves according to the description above, and uses an array. We discuss arrays in the rest of this section.

Besides the array, the program below uses some constructs that deserve a few comments:

  • On line 11, the for statement has an empty update statement (notice that there is nothing between the last semicolon ; and the closing parenthesis ) on that line). This simply means that the for loop does not execute any update statement after the loop body — it just checks the loop condition again. The loop variable i is updated on line 32 (but only if the condition !isDuplicate on line 28 is true).

  • On line 17, the for loop body uses a continue statement to “skip” the rest of the loop body for the current loop iteration, as described in The continue Statement.

  • On lines 21 and 33, the program uses the increment statements j++ and i++: they are equivalent to j = j + 1 and i = i + 1, respectively.

  • On line 25 there is a break statement: if executed, that statement only breaks the immediately surrounding for loop (lines 21–27), and the execution continues from line 28.

  • On lines 40–42 there is a special form of for loop that iterates over the values stored in an array: we will discuss it shortly.

  • On line 41, we use System.out.print(...) to print something on the console without adding a “newline” at the end. To see the difference between System.out.print(...) and System.out.println(...), try the following on the Java shell:

    System.out.print("Output 1 "); System.out.println("Output 2");
    

    and compare its output with the output produced by:

    System.out.println("Output 1 "); System.out.println("Output 2");
    
 1class Lotto {
 2    public static void main(String[] args) {
 3        var scanner = new java.util.Scanner(System.in);
 4        scanner.useLocale(java.util.Locale.ENGLISH);
 5
 6        System.out.print("How many Lotto numbers do you want to play? ");
 7        var howMany = scanner.nextInt();
 8
 9        var numbers = new int[howMany]; // Array that will contain the numbers
10
11        for (var i = 0; i < numbers.length; ) {
12            System.out.print("Enter a number between 1 and 36: ");
13            var n = scanner.nextInt();
14
15            if ((n < 1) || (n > 36)) {
16                System.out.println("Number out of range!");
17                continue;
18            }
19
20            var isDuplicated = false; // Was the last number entered before?
21            for (var j = 0; j < i; j++) {
22                if (numbers[j] == n) {
23                    System.out.println("You have already entered " + n);
24                    isDuplicated = true;
25                    break;
26                }
27            }
28
29            if (!isDuplicated) {
30                numbers[i] = n;
31                var left = numbers.length - (i + 1);
32                System.out.println("OK, " + left + " numbers left");
33                i++; // Move on to read the next Lotto number
34            }
35        }
36
37        scanner.close();
38
39        System.out.print("You entered the numbers: ");
40        for (var n: numbers) {
41            System.out.print(n + " "); // Print without a final newline
42        }
43        System.out.println(""); // Print a final newline
44    }
45}

Arrays in Java: the Basics#

In Java, arrays are objects and are created using new, as follows: (as usual, we experiment on the Java shell)

1jshell> var ints = new int[5]
2ints ==> int[5] { 0, 0, 0, 0, 0 }
3|  created variable ints : int[]

In the example above, new int[5] creates a new array containing 5 values of type int; then, the new array is used to declare a variable that we have called ints. After that, the Java shell tells us that:

  • on line 2: the variable ints gives us access to an array that contains 5 values of type int — and each one of these values is 0. This is represented by the Java shell as { 0, 0, 0, 0, 0 };

  • on line 3: the variable ints has type int[], which is the type of an array containing values of type int.

In general, we can create arrays containing any number of values of the same type, and we can also choose that type freely. For instance, we can create an array of values of type double:

jshell> var doubles = new double[2 + 3];
doubles ==> double[5] { 0.0, 0.0, 0.0, 0.0, 0.0 }
|  created variable doubles : double[]

Notice that the number of values stored in the array can be any expression of type int, such as 2 + 3 above. Therefore, we can also write e.g. new double[n], where n is a variable whose value is not known while we write the code (e.g. because the value of n is read from the terminal when the program runs).

Remark 15 (Arrays filled with default values)

In the examples above, we have created the arrays ints and doubles without specifying which values they contain: consequently, Java has created the arrays by using default values — and such default values depend on the type of the array: for instance, an array of ints is filled with the value 0, while an array of doubles is filled with the value 0.0.

We can also create an array by specifying which values it contains:

1jshell> var speeds = new double[] {10.0, 18.2, 42.3}
2speeds ==> double[3] { 10.0, 18.2, 42.3 }
3|  created variable speeds : double[]

Notice line 2 above: it says that the array in the variable speeds contains the three values we have specified when creating the array object (on line 1).

Note

If we create an array by explicitly providing the array contents (as we did when defining the variable speeds above), we cannot specify at the same time the length of the array — i.e. writing new double[3] {10.0, 18.2, 42.3} gives an error. (Try it!)

After an array object has been created, we can see how many values it contains by accessing its length field, which contains a value of type int: (we will see what an “object field” is exactly later in the course)

jshell> System.out.println("The length of 'ints' is: " + ints.length)
The length of 'ints' is: 5
jshell> System.out.println("The length of 'speeds' is: " + speeds.length)
The length of 'speeds' is: 3

Important

The length of an array is fixed when the array is created, and cannot be changed afterwards.

To access a value stored in an array, we use the index of the value, i.e. the position of the value insude the array, counting from 0. To do that, we use the notation:

array_expr[int_expr]

where:

  • array_expr is any expression that produces an array (e.g. a variable of type array, such as speeds above), and

  • int_expr is any expression of type int.

For example: (remember that the array in the variable speeds is { 10.0, 18.2, 42.3 })

jshell> System.out.println("The value of speeds[0] is: " + speeds[0])
The value of speeds[0] is: 10.0
jshell> System.out.println("The value of speeds[1] is: " + speeds[1])
The value of speeds[1] is: 18.2
jshell> System.out.println("The value of speeds[2] is: " + speeds[2])
The value of speeds[2] is: 42.3

We can also change a value stored in an array, by performing an assignment that replaces the value with a specified index. For example, if we try:

jshell> speeds[1] = 1234.5
...

and we display again the contents of the array speeds on the Java shell, we will notice that its second value (i.e. the value with index 1) is now 1234.5 (while it was 18.2 before).

jshell> speeds
speeds ==> double[3] { 10.0, 1234.5, 42.3 }
|  value of speeds : double[]

Important

If we try to access an array value in a position that is beyond the length of the array, we get an error, as an exception. You can see it for yourself: try to access e.g. speeds[3] on the Java shell! (Remember that array indexes are counted from 0, so speeds[3] is trying to access the 4th element of the array.)

Looping over Array Contents: the For-Each Statement#

To loop over all the values stored in an array, we can use e.g. a for loop, by incrementing an index and making sure that it does not go beyond the last index of the array. For example: (Try it on the Java shell!)

for (var i = 0; i < speeds.length; i++) {
    System.out.println("The value of speeds[" + i + "] is: " + speeds[i]);
}

Important

Since the indexes of an array are counted from 0, the last value of an array arr is stored at position arr.length - 1. For this reason, before running the loop body, the for loop above checks that the index i is less than the array length: when i becomes equal to the array length, the loop condition evaluates to false (which mean that the index i is beyond the last array index), and the loop ends.

Looping over all values stored in an array is a very common programming pattern, and it is quite easy to make mistakes when writing the code that increments and checks the array index. For this reason, Java offers a specialised form of loop, called for-each statement (or enhanced for loop), designed to simplify loops over array elements, and avoid mistakes. The syntax is:

for (var x: array_expr) {
    // Loop body, repeated for each value of the array produced by array_expr
}
// Statements executed after the loop ends

The for-each loop is executed as follows:

  • first, array_expr is evaluated, and it must produce an array;

  • then, for each value stored in the array produced by array_expr:

    • the current array value is assigned to the variable x; and

    • the loop body is executed, and it can use the variable x to access the current array value;

  • finally, after the last array value has been used, the execution continues with the statements (if any) that follow the loop.

The for-each loop uses the the array values in order, based on their index.

For example: (Try it on the Java shell!)

for (var s: speeds) {
    System.out.println("The array 'speeds' contains the value: " + s);
}

The output of the for-each loop above is:

The array 'speeds' contains the value: 10.0
The array 'speeds' contains the value: 1234.5
The array 'speeds' contains the value: 42.3

Important

The for-each loop provides access to each value stored in an array by copying the current value in the loop variable. Consequently, if you change the loop variable, the change is not reflected in the array! For example, consider: (using the array speeds from the previous examples)

for (var s: speeds) {
    System.out.println("The value of variable 's' is: " + s);
    s = 42.0;
    System.out.println("The value of variable 's' is now: " + s);
}

If you execute the loop above on the Java shell, you will see that the value of the variable s changes at each loop — and when the variable is assigned the value 42.0, the assignments only modifies s, not the content of the array speeds. You can verify this fact by visualising the contents of speeds before and after executing the loop above.

Using Arrays: Some Examples#

This section contains some common examples array usage.

  • Splitting a string into an array of strings (Example 35)

  • Static methods taking arrays as arguments (Example 36)

Example 35 (Splitting a string into an array of strings)

We have already seen some useful String methods, but String objects offer much more. For instance, the String object method split(separator) computes and returns an array of String objects; each object stored in that array, in turn, is a sub-string of the original string, obtained by splitting the original string using the given separator.

For example, if you want to split a string around each space " ", you can try:

jshell> "This is a test".split(" ")
$1 ==> String[4] { "This", "is", "a", "test" }
|  created scratch variable $1 : String[]

Or, if you want to split a string around each occurrence of ", " (i.e. a comma followed by a space):

jshell> "This is a sentence, and this is another sentence".split(", ")
$2 ==> String[2] { "This is a sentence", "and this is another sentence" }
|  created scratch variable $2 : String[]

Note

The split(...) method provided by String objects is very flexible, because the “separator” is actually a regular expression that can specify rather complex splitting patterns. You don’t need to learn regular expressions in this course, but you will get an error if you try to split a string using a separator that happens to be an invalid regular expression: try e.g. "Hello".split("[") on the Java shell.

Example 36 (Arrays as method arguments)

In Java we can write static methods that take arrays as arguments. For example, the following static method returns the sum of all values contained in its argument values — which has type int[], and thus, is an array of integers. Observe that the program uses a for-each loop to examine all array elements. (Try copy&pasting this code snippet on the Java shell!)

static int sum(int[] values) {
    var sum = 0; // Will contain the sum of all elements of 'values'    

    for (var v: values) {
        sum = sum + v;
    }

    return sum;
}

You can try the static method above on the Java shell. For instance, you can create an array of integers called arr

jshell> var arr = new int[] {1, 2, 3, 4}
arr ==> int[4] { 1, 2, 3, 4 }
|  created variable arr : int[]

…and then call sum(arr) to compute the sum of all its values.

jshell> System.out.println("The sum of the values in 'arr' is: " + sum(arr))
The sum of values in 'arr' is: 10

Concluding Remarks#

You should now have an understanding of how to define and use arrays to handle collections of a fixed number of values (each one having the same type). With this knowledge, you should be able to understand each line of the programs in Example 34.

References and Further Readings#

You are invited to read the following sections of the reference book: (Note: they sometimes mention Java features that we will address later in the course)

  • Section 8.1 - “Array Elements”

  • Section 8.2 - “Declaring and Using Arrays”

Exercises#

Note

These exercises are not part of the course assessment. They are provided to help you self-test your understanding.

Exercise 25 (Some practice with arrays)

Try the following tasks:

  • Create an array containing values of type int, containing 5 numbers from 1 to 5.

  • Create an array containing 5 values of type int, filled with the default value 0. Then, modify the values stored in the array, so that, after your modifications, the array contains the numbers from 1 to 5.

  • Create an array containing 1000 values of type double, filled with the default value 0.0.

  • Create an array containing 1000 values of type double, filled with the default value 0.0. Then, modify the values stored in the array, so that, after your modifications, the array contains the numbers from 1.0 to 1000.0 (NOTE: you might want to use a loop to perform this task…).

Lab and Weekly Assessments#

During the lab you can try the exercises above or begin your work on the weekly assessments below.

Important

01 - Count the Occurrences#

Your task is to edit the file Utils.java provided in the handout. In the class Utils, implement the static method with the following signature:

static int count(int v, int[] values)

The static method must count how many times the value v occurs in the array values, and return the result. Note that values might have 0 elements.

The static method Utils.count(...) is used in the test program contained in the file Occurrences.java, which is also included in the handout: you should read Occurrences.java and try to run it, but you must not modify it.

When you are done, submit the modified file Utils.java on DTU Autolab.

Important

Besides testing your solution by running ./grade, make sure you are also able to manually compile this multi-file Java program (using javac) and then run it (using java), as explained in Splitting Code into Separate Files.

Warning

The automatic grading on DTU Autolab includes some additional secret checks that test your submission with additional inputs. After you submit, double-check your grading result on DTU Autolab: if the secret checks fail, then your solution is not correct, and you should fix it and resubmit.

02 - Maximum 3#

This is a variation of the assessment 05 - Maximum 2. Your task is to edit the file Utils.java provided in the handout. In the class Utils, implement a static method that:

  • is named max;

  • takes one argument: an array of values of type double;

  • returns a value of type double.

The static method Utils.max(...) must return the maximum among the values contained in the array passed as argument. You can assume that the array passed as argument has length 1 or greater.

The static method Utils.max(...) is used in the test program contained in the file Maximum.java, which is also included in the handout: you should read Maximum.java and try to run it, but you must not modify it.

When you are done, submit the modified file Utils.java on DTU Autolab.

Important

Besides testing your solution by running ./grade, make sure you are also able to manually compile this multi-file Java program (using javac) and then run it (using java), as explained in Splitting Code into Separate Files.

Warning

The automatic grading on DTU Autolab includes some additional secret checks that test your submission with additional inputs. After you submit, double-check your grading result on DTU Autolab: if the secret checks fail, then your solution is not correct, and you should fix it and resubmit.

Hint

To see how to write a static method that takes an array as argument, see Example 36. The signature of the static method count(...) in 01 - Count the Occurrences may also be helpful.

03 - Even Numbers#

Edit the file Evens.java provided in the handout, and write a program that reads from the console a whole line containing one or more integer numbers separated by “, ” (i.e. a comma and a space), and prints how many of those numbers are even.

For instance, if the line read from the console is:

1, 32, 4, 12, 5, 66

then the program must print:

4

When you are done, submit the modified file Evens.java on DTU Autolab.

Hint

The file Evens.java already contains some code that reads a line from the console, and turns it into an array of integers (after splitting the line as shown in Example 35).

04 - Statistics#

Edit the file Statistics.java provided in the handout, and write a program that reads from the console a whole line containing one or more numbers separated by “; ” (i.e. a semicolon and a space), and prints their mean and standard deviation. The numbers may contain decimal digits, and should be handled as doubles.

For instance, if the line read from the console is:

1.0; 2; 3; 4.0

then the program must print:

Mean: 2.5
Standard deviation: 1.118033988749895

Remember that, if we have a set of \(n\) input values \(x_1, \ldots, x_n\), then their mean \(\bar{x}\) is computed as:

\[ \bar{x} \;=\; \frac{1}{n}\sum_{i = 1}^n x_i \]

and their standard deviation \(\sigma\) (greek letter “sigma”) is computed as:

\[ \sigma \;=\; \sqrt{\frac{1}{n} \sum_{i = 1}^n(x_i - \bar{x})^2} \]

When you are done, submit the modified file Statistics.java on DTU Autolab.

Hint

  • You can reuse and adapt some code from the file Evens.java provided with 03 - Even Numbers.

  • To obtain a double from a String, you can use Double.parseDouble(...). For instance, Double.parseDouble("3.14") produces the value 3.14 (of type double).

  • To compute the power and the square root in the standard deviation formula, you can use Math.pow(...) and Math.sqrt(...), respectively. For instance:

    jshell> Math.pow(3.0, 2)
    $1 ==> 9.0
    |  created scratch variable $1 : double
    
    jshell> Math.sqrt(25.0)
    $2 ==> 5.0
    |  created scratch variable $2 : double