Section 9.7. Two-Dimensional Arrays


[Page 430]

9.7. Two-Dimensional Arrays

A two-dimensional array, an array whose components are themselves arrays, is necessary or useful for certain kinds of problems. For example, you would use this type of array if you are doing a scientific study in which you have to track the amount of precipitation for every day of the year.

One way to organize this information would be to create a one-dimensional array consisting of 365 elements:

double rainfall[] = new double[365]; 


However, this representation would make it very difficult to calculate the average rainfall within a given month, which might be an important part of your study.

A better representation for this problem would be to use a two-dimensional array, one dimension for the months and one for the days. The following statement declares the array variable rainfall and creates a 12 x 31 array object as its reference:

double rainfall[][] = new double[12][31]; 


What data do we need?


Thus, rainfall is an array of arrays. You can think of the first array as the 12 months required for the problem. And you can think of each month as an array of 31 days. The months will be indexed from 0 to 11, and the days will be indexed from 0 to 30.

The problem with this representation is that when we want to refer to the rainfall for January 5, we would have to use rainfall[0][4]. This is awkward and misleading. The problem is that dates1/5/1999are unit indexed, while arrays are zero indexed. Because it will be difficult to remember this fact, our representation of the rainfall data may cause us to make errors when we start writing our algorithms.

Choosing an appropriate representation


We can easily remedy this problem by just defining our array to have an extra month and an extra day each month:

double rainfall[][] = new double[13][32]; 


This representation creates an array with 13 months, indexed from 0 to 12, with 32 days per month, indexed from 0 to 31. However, we can simply ignore the 0 month and 0 day by using unit indexing in all of the algorithms that process the array. In other words, if we view this array as a two-dimensional table consisting of 13 rows and 32 columns, we can leave row 0 and column 0 unused (Fig. 9.19).

Figure 9.19. A two-dimensional array with 13 rows and 32 columns. To represent 12 months of the year, we can simply ignore row 0 and column 0.
(This item is displayed on page 431 in the print version)


As Figure 9.19 shows, the very first element of this 416-element array has subscripts (0,0), while the last location has subscripts (12,31). The main advantages of this representation are that the program as a whole will be much easier to read and understand and much less prone to error.

Effective Design: Readability

To improve a program's robustness and readability, it may be preferable to use unit array indexing by declaring extra array elements and ignoring those with index 0.



[Page 431]

In order to refer to an element in a two-dimensional array, you need to use two subscripts. For the rainfall array, the first subscript will specify the month and the second will specify the day within the month. Thus, the following statements assign 1.15 to the rainfall element representing January 5, and then print its value:

rainfall[1][5] = 1.15;   // Rainfall for January 5 System.out.println( rainfall[1][5] ); 


Referencing two-dimensional arrays


Just as in the case of one-dimensional arrays, it is an error to attempt to reference an element that is not in the array. Each of the following examples would cause Java to raise an IndexOutOfBoundsException:

rainfall[13][32] = 0.15 ;  // No such element rainfall[11][33] = 1.3;    // No such column rainfall[14][30] = 0.74;   // No such row 


If the initial values of an array's elements are supposed to be zero, there is no need to initialize the elements. Java will do it automatically when you create the array with new. However, for many array problems it is necessary to initialize the array elements to some other value. For a two-dimensional array, this would require a nested loop. To illustrate this algorithm, let's use a nested for loop to initialize each element of the rainfall array to 0:

// Note that both loops are unit indexed. for (int month = 1; month < rainfall.length; month++)   for (int day = 1; day < rainfall[month].length; day++)      rainfall[month][day] = 0.0; 


Initializing two-dimensional arrays


Note that both for loops use unit indexing. This is in keeping with our decision to leave month 0 and day 0 unused.

Remember that when you have a nested for loop, the inner loop iterates faster than the outer loop. Thus, for each month, the inner loop will iterate over 31 days. This is equivalent to processing the array as if you were going across each row and then down to the next row in the representation shown in Figure 9.19.

Nested for loops



[Page 432]

Note that for a two-dimensional array, both dimensions have an associated length variable, which is used in this example to specify the upper bound of each for loop. For the rainfall array, the first dimension (months) has a length of 13, and the second dimension (days) has a length of 32.

Another way to view the rainfall array is to remember that it is an array of arrays. The length of the first array, which corresponds to the number of months (13), is given by rainfall.length. The length of each month's array, which corresponds to the number of days in a month (32), is given by rainfall[month].length.

Array of arrays


The outer loop of the nested for loop iterates through months 1 through 12, and the inner for loop iterates through days 1 through 31. In this way, 372 = 12 x 31 elements of the array are set to 0.0. In Table 9.1, the boldface numbers along the top represent the day subscripts, while the boldface numbers along the left represent the month subscripts.

Table 9.1. Initialized rainfall array. Unused array elements are shown as dashes.
 

0

1

2

3

30

31

0

1

0.0

0.0

0.0

0.0

0.0

2

0.0

0.0

0.0

0.0

0.0

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

10

0.0

0.0

0.0

0.0

0.0

11

0.0

0.0

0.0

0.0

0.0

12

0.0

0.0

0.0

0.0

0.0


Self-Study Exercises

Exercise 9.14

Declare a two-dimensional array of int, named int2d, that contains five rows, each of which contains 10 integers.

Exercise 9.15

Write a statement that prints the last integer in the third row of the array that you created in the preceding exercise. Then write an assignment statement that assigns 100 to the last element in the int2d array.

Exercise 9.16

Write a loop to print all of the elements of int2d, declared in the preceding exercise. Print one row per line with a space between each element on a line.

9.7.1. Two-Dimensional Array Methods

Now that we have figured out how to represent the data for our scientific experiment, let's develop methods to calculate some results. First, we want a method to initialize the array. This method will simply incorporate the nested loop algorithm we developed previously:

public void initRain(double rain[][]) {     for (int month = 1; month < rain.length; month++)         for (int day = 1; day < rain[month].length; day++)             rain[month][day] = 0.0; } // initRain() 



[Page 433]

Note how we declare the parameter for a multidimensional array. In addition to the element type (double) and the name of the parameter (rain), we must also include a set of brackets for each dimension of the array.

Array parameters


Note also that we use the parameter name within the method to refer to the array. As with one-dimensional arrays, the parameter is a reference to the array, which means that any changes made to the array within the method will persist when the method is exited.

The avgDailyRain() Method

One result that we need from our experiment is the average daily rainfall. To calculate this result, we would add up all of the rainfalls stored in the 12 x 31 array and divide by 365. Of course, the array itself contains more than 365 elements. It contains 416 elements, but we're not using the first month of the array, and in some monthsthose with fewer than 31 dayswe're not using some of the day elements. For example, there's no such day as rainfall[2][30], which would represent February 30. However, because we initialized all of the array's elements to 0, the rainfall recorded for the nondays will be 0, which won't affect our overall average.

Algorithm design


The method for calculating average daily rainfall should take our two-dimensional array of double as a parameter, and it should return a double. Its algorithm will use a nested for loop to iterate through the elements of the array, adding each element to a running total. When the loops exits, the total will be divided by 365 and returned:

public double avgDailyRain(double rain[][]) {     double total = 0;     for (int month = 1; month < rain.length; month++)         for (int day = 1; day < rain[month].length; day++)             total += rain[month][day];     return total/365; } // avgDailyRain() 


Method design


The avgRainForMonth() Method

One reason we used a two-dimensional array for this problem is so we could calculate the average daily rainfall for a given month. Let's write a method to solve this problem. The algorithm for this method will not require a nested for loop. We will just iterate through the 31 elements of a given month, so the month subscript will not vary. For example, suppose we are calculating the average for January, which is represented in our array as month 1:

double total = 0; for (int day = 1; day < rainfall[1].length; day++)      total = total + rainfall[1][day]; 


Algorithm design


Thus, the month subscript is held constant (at 1) while the day subscript iterates from 1 to 31. Of course, in our method we would use a parameter to represent the month, thereby allowing us to calculate the average daily rainfall for any given month.

Method design


Another problem that our method has to deal with is that months don't all have 31 days, so we can't always divide by 31 to compute the monthly average. There are various ways to solve this problem, but perhaps the easiest is to let the number of days for the month be specified as a third parameter. That way, the month itself and the number of days for the month are supplied by the user of the method:


[Page 434]

public double avgRainForMonth(double rain[][], int month, int nDays) {     double total = 0;     for (int day = 1; day < rain[month].length; day++)         total = total + rain[month][day];     return total/nDays; } // avgRainForMonth() 


Method design: What data do we need?


Given this definition, we can call this method to calculate and print the average daily rainfall for March, as in the following statement:

System.out.println("March: " + avgRainForMonth(rainfall,3,31)); 


Note that when passing the entire two-dimensional array to the method, we just use the name of the array. We do not have to follow the name with subscripts.

9.7.2. Passing Part of an Array to a Method

Instead of passing the entire rainfall array to the avgRainForMonth() method, we could redesign this method so that it is only passed the particular month that is being averaged. Remember that a two-dimensional array is an array of arrays, so if we pass the month of January, we are passing an array of 32 days. If we use this approach, we need only two parameters: the month, which is an array of days, and the number of days in the month:

public double avgRainForMonth(double monthRain[], int nDays) {     double total = 0;     for (int day = 1; day < monthRain.length; day++)         total = total + monthRain[day];     return total/nDays; } // avgRainForMonth() 


Method design: What data?


Given this definition, we can call it to calculate and print the average daily rainfall for March, as in the following statement:

System.out.println("March: " + avgRainForMonth(rainfall[3],31)); 


In this case, we are passing an array of double to the method, but in order to reference it, we have to pull it out of the two-dimensional array by giving its row subscript as well. Thus, rainfall[3] refers to one month of data in the two-dimensional array, the month of March. But rainfall[3] is itself a one-dimensional array. Figure 9.20 helps to clarify this point.


[Page 435]

Figure 9.20. Referencing individual elements and array elements in a two-dimensional array.


Deciding whether to use brackets when passing data to a method is not just a matter of whether you are passing an array. It is a matter of what type of data the method parameter specifies. Whenever you call a method that involves a parameter, you have to look at the method definition to see what kind of data that parameter specifies. Then you must supply an argument that refers to that type of data.

Specifying an argument


For our two-dimensional rainfall array, we can refer to the entire array as rainfall. We can refer to one of its months as rainfall[j], where j is any integer between 1 and 12. And we can refer to any of its elements as rainfall[j][k], where j is any integer between 1 and 12, and k is any integer between 1 and 31.

Java Language Rule: Arguments and Parameters

The argument in a method call must match the data type in the method definition. This applies to all parameters, including array parameters.


The Rainfall class (Figs. 9.21 and 9.22) shows how we can test our array algorithms. It creates the rainfall array in the main() method. It then initializes the array and prints out average daily rainfall and average daily rainfall for the month of March. However, note that we have made a slight modification to the initRain() method. Instead of just assigning 0 to each element, we assign a random value between 0 and 2.0:

rain[month][day] = Math.random() * 2.0; 


Figure 9.21. The Rainfall class.
(This item is displayed on page 436 in the print version)


Figure 9.22. Definition of the Rainfall class.
(This item is displayed on pages 436 - 437 in the print version)

public class Rainfall {   /**    * Initializes the rainfall array    * @ param rain is a 2D-array of rainfalls    * Pre:  rain is non null    * Post: rain[x][y] == 0 for all x,y in the array    * Note that the loops use unit indexing.    */   public void initRain(double rain[][]) {     for (int month = 1; month < rain.length; month++)       for (int day = 1; day < rain[month].length; day++)         rain[month][day] = Math.random() * 2.0;    // Random rainfall   } // initRain()   /**    * Computes average daily rainfall for a year of rainfall data    * @ param rain is a 2D-array of rainfalls    * @ return The sum of rain[x][y] / 356    * Pre:  rain is non null    * Post: The sum of rain / 365 is calculated    * Note that the loops are unit indexed    */   public double avgDailyRain(double rain[][]) {     double total = 0;     for (int month = 1; month < rain.length; month++)       for (int day = 1; day < rain[month].length; day++)         total += rain[month][day];     return total/365;   } // avgDailyRain()   /**    * Computes average daily rainfall for a given month containing nDays    * @ param monthRain is a 1D-array of rainfalls    * @ param nDays is the number of days in monthRain    * @ return The sum of monthRain / nDays    * Pre:  1 <= nDays <= 31    * Post: The sum of monthRain / nDays is calculated    */   public double avgRainForMonth(double monthRain[], int nDays) {     double total = 0;     for (int day = 1; day < monthRain.length; day++)       total = total + monthRain[day];     return total/nDays;   } // avgRainForMonth() 
[Page 437]
public static void main(String args[]) { double rainfall[][] = new double[13][32]; Rainfall data = new Rainfall(); data.initRain(rainfall); System.out.println("The average daily rainfall = " + data.avgDailyRain(rainfall)); System.out.println("The average daily rainfall for March = " + data.avgRainForMonth(rainfall[3],31)); } // main() } // Rainfall class

Using the Math.random() method in this way enables us to generate some realistic test data. In this case, we have scaled the data so that the daily rainfall is between 0 and 2 inches. (Rainfall like this would probably be appropriate for an Amazonian rain forest!) Testing our algorithms with these data provides some indication that our methods are in fact working properly.

Generating test data



[Page 437]

Effective Design: Generating Test Data

The Math.random() method can be used to generate numeric test data when large amounts of data are required. The data can be scaled to fit within the expected range of the actual data.


Self-Study Exercises

Exercise 9.17

Suppose you're going to keep track of the daily newspaper sales at the local kiosk. Declare a 52 x 7 two-dimensional array of int and initialize each of its elements to 0.

Exercise 9.18

Write a method to calculate the average number of newspapers sold per week, using the array you declared in the preceding exercise.

Exercise 9.19

Write a method to calculate the average number of newspapers sold on Sundays, using the array you declared in the preceding exercise. Assume that Sunday is the last day of the week.




Java, Java, Java(c) Object-Orienting Problem Solving
Java, Java, Java, Object-Oriented Problem Solving (3rd Edition)
ISBN: 0131474340
EAN: 2147483647
Year: 2005
Pages: 275

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net