Two-dimensional Arrays

Array Initializers

Java offers a short-cut method for creating and initializing the contents of an array, the array initializer. The code below demonstrates how to declare an array reference variable and make it point to an array of pre-set values.

int[] X = {3, 2, 6, 8, 1, 9, 5, 4, 7};

An array created by means of an array initializer is a full-fledged array that can do anything a normal array can. The following code demonstates that this is the case.

public class ArrayInit {

  public static void printArray(int[] A) {
    for (int n = 0; n < A.length; n++) {
      System.out.printf("%5d", A[n]);
      if (n % 10 == 9) {
        System.out.println();
      } else {
        System.out.print(" ");
      }
    }
    System.out.println();
  }

  public static void main(String[] args) {

    int[] X = {3, 2, 6, 8, 1, 9, 5, 4, 7};

    System.out.println("Before sorting: ");
    printArray(X);

    Arrays.sort(X);

    System.out.println("After sorting: ");
    printArray(X);
  }
}

Two-dimensional arrays

Java makes it possible to create a two-dimensional array, which is an array with both rows and columns, like a grid of data values. The basic mechanism that Java uses to create a two-dimensional array is to implement the structure as an array of arrays. Two sets of arrays are needed to create this structure. The first element is an array of array reference variables.

Normally, when we create an array we create two entities that work together to implement the array construct. The first entity is the array reference variable, which stores location information that points to the actual array. The second entity is the array itself. For example, in the following declaration we create an array reference variable named 'A' and an array for A to point to.

int[] A = new int[100];

The declaration

int[] A;

sets up the array reference variable, while the expression

new int[100]

creates the actual array and returns location information for the newly created array.

More abstractly, what is going on here is that we are selecting an underlying type to use for the array elements, creating an array of elements of that type, and setting up a reference variable to point to the array. That is, the expression above is a specific version of a more generic process that takes the general form

<type>[] A = new <type>[100];

where <type> can be any valid data type.

Up until this point in our discussion of arrays, we have only used primitive types such as int, double, or String as the underlying types for the arrays we create. A somewhat more sophisticated thing we can do is to use an array reference type as the underlying type for the array we create. The following example shows how this would work.

int[][] T = new int[100][];

The underlying type used for the array T is int[], which is the type normally used for array reference variables meant to point to an array of ints.

Creating the array T simply gives us an array of cells where each cell can point to an array of integers. Creating T does nothing to produce those other arrays, that step typically has to be done as a separate operation:

for(int n = 0;n<100;n++)
  T[n] = new int[10];

Filling all of the arrays we just created with values requires further work. For example, if we want to fill every cell in the two-dimensional structure we just created with the value 0, we would have to do

for(int col = 0;col < 100;col++)
  for(int row = 0;row < 10;row++)
    T[col][row] = 0;

The nomenclature used for the loop variables here is suggestive of how to interpret the structure we just built. The array T is effectively an array that holds references to the 100 columns of a two-dimensional grid structure. The elements of those columns are accessed via row numbers that range from 0 to 9.

Using array initializers to initialize a two-dimensional array

The array initializer syntax we showed above can be generalized in a natural way to create and initialize a two-dimensional array.

int[][] C = {{1,2},{3,3},{4,2}};

The structure created by this statement is a two-dimensional array with three columns and two rows. To access the item with value 4 in column 2 and row 0 we would use the syntax

C[2][0]

The closest point example

Chapter 6 in the textbook shows an example of using a two-dimensional array to solve the problem of finding which pair of points in a list of points are closest together. Here is the code for that example.

public class FindNearestPoints {
  public static void main(String[] args) {
    // Each row in points represents a point
    double[][] points = {{-1, 3}, {-1, -1}, {1, 1},
      {2, 0.5}, {2, -1}, {3, 3}, {4, 2}, {4, -0.5}};

    // p1 and p2 are the indices in the points array
    int p1 = 0, p2 = 1; // Initial two points
    double shortestDistances = distance(points[p1][0], points[p1][1],
    points[p2][0], points[p2][1]); // Initialize shortestDistances

    // Compute distance for every two points
    for (int i = 0; i < points.length; i++) {
      for (int j = i + 1; j < points.length; j++) {
        double distance = distance(points[i][0], points[i][1],
          points[j][0], points[j][1]); // Find distance

        if (shortestDistances > distance) {
          p1 = i; // Update p1
          p2 = j; // Update p2
          shortestDistances = distance; // Update shortestDistances
        }
      }
    }

    // Display result
    System.out.println("The closest two points are " +
      "(" + points[p1][0] + ", " + points[p1][1] + ") and (" +
      points[p2][0] + ", " + points[p2][1] + ")");
  }

  /** Compute the distance between two points */
  public static double distance(
      double x1, double y1, double x2, double y2) {
    return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
  }
}

The array points declared in main is a two-dimensional array of doubles. Each column in points is an array of two doubles representing one of the points. The double loop structure in main searches through all possible pairs of points looking for the pair that are closest together. When it finds a pair that are closer together than the closest pair yet seen, it records the indices p1 and p2 of those two points in the points array.

Using file data to initialize a two-dimensional array

Here is another version of the closest point program. In this version, we initialized the points array with data read from a file. The method readPointsFromFile is responsible for creating the individual columns of the points array, each of which is an array of two doubles representing a point.

package nearestPoints;

import java.util.Scanner;
import java.io.File;

public class FindNearestPointsFile {
  /* Open the file with the given name and count how many 
  numbers it contains. */

  public static int countNumbersInFile(String fileName) {
    int count = 0;
    try {
      File inFile = new File(fileName);
      Scanner input = new Scanner(inFile);

      while (input.hasNextDouble()) {
        double dummy = input.nextDouble();
        count++;
      }
      input.close();
    } catch (Exception ex) {
      System.out.println("Unable to open the file " + fileName + ".");
    }
    return count;
  }

  /* Fill the array A with points read from the file with the 
  given fileName. The array A should be sized to match the 
  number of points in the file. */
  public static void readPointsFromFile(double[][] A, String fileName) {
    try {
      File inFile = new File(fileName);
      Scanner input = new Scanner(inFile);

      for (int n = 0; n < A.length; n++) {
        // Each point we read is represented as an array of two 
        // doubles. Create the array and fill it with data.
        A[n] = new double[2];
        A[n][0] = input.nextDouble();
        A[n][1] = input.nextDouble();
      }
      input.close();
    } catch (Exception ex) {
      System.out.println("Unable to open the file " + fileName + ".");
    }
  }

  public static void main(String[] args) {
    int N = countNumbersInFile("points.txt");
    // Since each point requires two doubles to describe its location,
    // we need to make our array of points have size equal to half the
    // number of doubles we just saw in the input file.
    double[][] points = new double[N / 2][];
    // The work of actually creating the arrays for each point and
    // reading the data for that point is done by the next method.
    readPointsFromFile(points, "points.txt");

    // p1 and p2 are the indices in the points array
    int p1 = 0, p2 = 1; // Initial two points
    double shortestDistances = distance(points[p1][0], points[p1][1],
        points[p2][0], points[p2][1]); // Initialize shortestDistances

    // Compute distance for every two points
    for (int i = 0; i < points.length; i++) {
      for (int j = i + 1; j < points.length; j++) {
        double distance = distance(points[i][0], points[i][1],
            points[j][0], points[j][1]); // Find distance

        if (shortestDistances > distance) {
          p1 = i; // Update p1
          p2 = j; // Update p2
          shortestDistances = distance; // Update shortestDistances
        }
      }
    }

    // Display result
    System.out.println("The closest two points are " +
        "(" + points[p1][0] + ", " + points[p1][1] + ") and (" +
        points[p2][0] + ", " + points[p2][1] + ")");
  }

  /** Compute the distance between two points */
  public static double distance(
    double x1, double y1, double x2, double y2) {
    return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
  }
}

An alternative to two-dimensional arrays

As the discussion above illustrates, two-dimensional arrays are somewhat more complex than ordinary arrays. Given the added complexity of working with two-dimensional arrays, it is best to reserve their use for situations where there is no simpler alternative. In chapter 7 we start to learn about objects, which will provide us with another way to package and format data in a program. Frequently, objects will provide a cleaner and simpler alternative to using two-dimensional arrays. The closest point problem is a good example of how objects will make it possible for us to implement solutions more naturally.

The first step is to create a class to represent points. A Java class is essentially a container for data. Each point object that we will create will have to contain two pieces of data, the doubles for the x and y coordinates of the point. That data is stored in the point object as a pair of member variables of the Point class.

public class Point {

    public double x;
    public double y;

    public Point(double xCoord, double yCoord) {
        x = xCoord;
        y = yCoord;
    }
}

In addition to the member variables, the Point class also contains one method, called the constructor. The purpose of this method is to ensure that the data members get initialized properly. The constructor method gets called when we create a Point object. The code for declaring a Point variable and creating the object for that variable to point to is

Point p = new Point(2,0.5);

That statement creates a reference variable named 'p' that will point to a Point object. The expression

new Point(2.0,0.5)

creates the Point object for p to point to and gives it x and y coordinates of 2.0 and 0.5.

Once we have created a Point object, we can access its data members x and y by syntax like the following:

System.out.println("(" + p.x + "," + p.y + ")");

p.x gives us the x coordinate of the Point p, while p.y yields the y coordinate of p. We can use the same syntax to modify the coordinates of the Point p.

p.x = p.x + 2.0;
p.y = p.y + 2.0;

With the Point class as defined above, we can now re-write the 'find the closest points' example from above to do its work with an array of Point objects instead of a two-dimensional array. As we get further into chapter 7 and you get more experience working with objects, this approach will seem much more natural and logical than the approach that relies on a two-dimensional array of doubles to represent the points.

import java.util.Scanner;
import java.io.File;

public class FindNearestPointsClass {
  /* Open the file with the given name and count how many 
     numbers it contains. */

  public static int countNumbersInFile(String fileName) {
    int count = 0;
    try {
       File inFile = new File(fileName);
       Scanner input = new Scanner(inFile);

       while (input.hasNextDouble()) {
         double dummy = input.nextDouble();
         count++;
         }
         input.close();
       } catch (Exception ex) {
         System.out.println("Unable to open the file " + fileName + ".");
       }
       return count;
    }

  /* Fill the array A with points read from the file with the 
  given fileName. The array A should be sized to match the 
  number of points in the file. */
  public static void readPointsFromFile(Point[] A, String fileName) {
    try {
      File inFile = new File(fileName);
      Scanner input = new Scanner(inFile);

      for (int n = 0; n < A.length; n++) {
        // Each point we read must be initialized with two doubles 
        // read from the file.
        double x = input.nextDouble();
        double y = input.nextDouble();
        A[n] = new Point(x, y);
        }
      input.close();
     } catch (Exception ex) {
      System.out.println("Unable to open the file " + fileName + ".");
     }
  }

  public static void main(String[] args) {
    int N = countNumbersInFile("points.txt");
    Point[] points = new Point[N / 2];
    readPointsFromFile(points, "points.txt");

    // p1 and p2 are the indices in the points array
    int p1 = 0, p2 = 1; // Initial pair of points
    // Initialize shortestDistances
    double shortestDistances = distance(points[p1], points[p2]); 

    // Compute distance for every two points
    for (int i = 0; i < points.length; i++) {
      for (int j = i + 1; j < points.length; j++) {
        double distance = distance(points[i], points[j]); // Find distance
        if (shortestDistances > distance) {
          p1 = i; // Update p1
          p2 = j; // Update p2
          shortestDistances = distance; // Update shortestDistances
          }
        }
      }

    // Display result
    System.out.println("The closest two points are " +
      "(" + points[p1].x + ", " + points[p1].y + ") and (" +
      points[p2].x + ", " + points[p2].y + ")");
    }

  /** Compute the distance between two points */
  public static double distance(Point one, Point two) {
    double xDiff = two.x - one.x;
    double yDiff = two.y - one.y;
    return Math.sqrt(xDiff * xDiff + yDiff * yDiff);
    }
}