Principle 9.7.2. 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.
double rainfall[] = new double[365];
rainfall
and creates a 12 by 31 array object as its reference:double rainfall[][] = new double[12][31];
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.rainfall[0][4]
. This is awkward and misleading. The problem is that dates—1/5/1999—are 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.double rainfall[][] = new double[13][32];
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] );
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
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;
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.rainfall
array is to remember that it is an array of arrays. The length of the first array, which corresponds to the number (13) of months, is given by rainfall.length
. The length of each month’s array, which corresponds to the number of days (32) in a month, is given by rainfall[month].length
.rainfall
array. The 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 |
int
, named int2d
, that contains five rows, each of which contains ten integers.int2d
array.int2d
, one row per line with a space between each element on a line.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()
double
), and the name of the parameter (rain
), we must also include a set of brackets for each dimension of the array.avgDailyRain()
Method
rainfall[2][30]
, which would represent February 30.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()
avgRainForMonth()
Method
double total = 0;
for (int day = 1; day < rainfall[1].length; day++)
total = total + rainfall[1][day];
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()
System.out.println("March: " +
avgRainForMonth(rainfall,3,31));
avgRainForMonth()
method, we could redesign this method so that it is only passed the particular month that’s 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 array of days, and the number of days in that 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()
System.out.println("March: " +
avgRainForMonth(rainfall[3],31));
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.7.4 helps to clarify this point.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.Rainfall
class.Rainfall
class (Figure 9.7.6 and Listing 9.7.7) 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. rain[month][day] = Math.random() * 2.0;
Rainfall
class.initRain()
method. Instead of just assigning 0 to each element, we assign a random value between 0 and 2.0: Using the Math.random()
method in this way enables us to generate some realistic test data.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 range that the actual data are expected to have.int
.50 + (int)(Math.random()*150)