THINK FIRST·CODE LATER

← Java Programming
Chapter 9 · Week 12–14

Arrays and Two-Dimensional Arrays

Before You Start: What You Must Be Able to Do

Before the questions, make sure you can: declare and create an array in every legal form; state the valid index range and the default value of each element type; traverse an array with a for and with a for-each loop, and say when the for-each loop cannot be used; manage a partially filled array; copy an array correctly (and explain why assignment does not copy); write linear search, binary search and one sorting algorithm; and work with a two-dimensional array, including a.length versus a[i].length.

Declaring, creating and initialising

int[] marks;                        // declaration only: marks is null
marks = new int[5];                 // creation: 5 elements, all 0
int[] scores = new int[5];          // the usual one-line form
int[] data   = {4, 8, 15, 16, 23};  // initialiser list: size is taken from the list
String[] names = new String[3];     // three null references
double[][] grid = new double[3][4]; // 3 rows, 4 columns

An array is an object, so an array variable is a reference. Its size is fixed at creation and available as the field marks.length — no parentheses, unlike String.length(). Elements are automatically initialised: 0 for numeric types, false for boolean, null for reference types.

Valid indices run from 0 to length - 1. Any other index throws ArrayIndexOutOfBoundsException at run time — it is not a compile-time error, because the index is usually only known while the program runs.

Traversing

for (int i = 0; i < marks.length; i++)  System.out.println(i + ": " + marks[i]);
for (int m : marks)                     System.out.println(m);   // for-each

The for-each loop is shorter and safer, but it gives you a copy of each element and no index: you cannot use it to modify the array's primitive elements, to walk backwards, or to work with two arrays in step.

Partially filled arrays

When the eventual number of values is unknown, create an array large enough and keep a count of how many positions are in use. Every loop then runs to count, not to length, and count is the index at which the next value is stored.

Copying and aliasing

int[] a = {1, 2, 3};
int[] b = a;                       // NOT a copy: b and a refer to the same array
b[0] = 99;                         // a[0] is now 99 as well

int[] c = new int[a.length];
for (int i = 0; i < a.length; i++) c[i] = a[i];   // manual copy
int[] d = Arrays.copyOf(a, a.length);             // library copy
System.arraycopy(a, 1, d, 0, 2);   // copy 2 elements from a[1] into d[0], d[1]

System.arraycopy(src, srcPos, dest, destPos, length) is the one to read carefully in exam questions: it changes only length positions of the destination and leaves the rest alone.

Arrays and methods

An array parameter receives a copy of the reference, so a method can change the caller's elements. A method may also return an array (public static int[] doubled(int[] a)). Use arr.length inside the method rather than passing the size separately.

Searching

// Linear search: works on any array; O(n)
public static int linearSearch(int[] a, int key) {
    for (int i = 0; i < a.length; i++) if (a[i] == key) return i;
    return -1;                                    // conventional "not found"
}

// Binary search: requires a SORTED array; O(log n)
public static int binarySearch(int[] a, int key) {
    int low = 0, high = a.length - 1;
    while (low <= high) {
        int mid = (low + high) / 2;
        if (a[mid] == key)      return mid;
        else if (a[mid] < key)  low  = mid + 1;
        else                    high = mid - 1;
    }
    return -1;
}

Sorting

Selection sort: repeatedly find the smallest remaining element and swap it into place. Bubble sort: repeatedly compare neighbours and swap, so large values “bubble” to the end. Insertion sort: take each element and insert it into the already-sorted prefix. All three are O(n²); the library method Arrays.sort(a) is the practical choice.

for (int i = 0; i < a.length - 1; i++) {          // selection sort
    int min = i;
    for (int j = i + 1; j < a.length; j++) if (a[j] < a[min]) min = j;
    int t = a[i]; a[i] = a[min]; a[min] = t;      // swap needs a temporary
}

Two-dimensional arrays

int[][] m = new int[3][4];         // 3 rows, 4 columns; m.length is 3, m[0].length is 4
int[][] t = {{1,2,3},{4,5,6}};     // initialiser list
for (int r = 0; r < m.length; r++) {
    for (int c = 0; c < m[r].length; c++) System.out.print(m[r][c] + " ");
    System.out.println();
}
int[][] jagged = new int[3][];     // rows of different lengths are legal
jagged[0] = new int[2];

A 2-D array is really an array of arrays: m[r] is itself an array. Always use m[r].length for the inner bound. The only legal creation syntax is new int[2][2], never new int[2, 2].

Useful library calls: Arrays.toString(a) for a 1-D array, Arrays.deepToString(m) for a 2-D array, Arrays.sort, Arrays.fill, Arrays.equals.

Remember
  • array.length is a field; string.length() is a method.
  • Indices run from 0 to length - 1; anything else throws at run time.
  • Assigning one array variable to another creates an alias, not a copy.
  • A method can modify the caller's array elements.
  • Binary search requires a sorted array.
  • For a 2-D array, m.length is the number of rows and m[r].length the length of row r.
Common Pitfalls
  • for (int i = 0; i <= a.length; i++) — one index too far.
  • int[] a = new int[3]; a = {1,2,3}; — an initialiser list may only be used in the declaration.
  • Printing an array with System.out.println(a): it prints a hash code, not the elements. Use Arrays.toString.
  • Forgetting that elements of a String[] start as null.

Ready? Close the notes and practise.

40 questions. Predict the output before you check — that is the skill the exam measures.