In the previous lecture, we saw that array structures can be defined over any class of object instances, i.e. these are not restricted to the basic primitive types.
In this lecture we consider a further development of arrays - multi-dimensional array structures.
The form of arrays encountered, so far, has been 1-dimensional. In other words, the declaration of a 10 element array of integers, is realised by
int [] TenIntegerArray = new int[10] |
This results in the allocation of 10 locations indexed from 0 through 9, thus:
Figure 2.1: Allocation of Memory in 1-dimensional 10 element Array
In 1-dimensional arrays, any individual element within the array can be referred to simply
by specifying its offset index, e.g.
the element with index 4 (the fifth location since the first item
is indexed by 0) of TenIntegerArray
is TenIntegerArray[4] as shown in Figure 2.1.
While 1-dimensional array structures (whether constrained or
unconstrained)
suffice to model data representations in a number of applications, there are many
cases where it is more natural to think of specific items of data in some ordered
collection of data as being referenced by 2,3 or more indices.
Here is a selection of example situations:
Figure 2.2
In this case, the data to be held are more naturally viewed as:
rather than as,
is naturally viewed as an 8-by-8 table of values in which each element corresponds to a square
of the board and contains one of 5 values to encode: `the square is empty', `the square has a black/white
crowned/uncrowned piece occupying it'. So the opening position above might be represented by
where: 'E' indicates an Empty square; 'BP' one occupied by an uncrowned Black piece; 'WP'
a square occupied by an uncrowned White piece.
Of course, in principle, one could represent such structures by using an appropriate
1-dimensional array within a suitable Class
definition, among the methods of which would be mechanisms for accessing a specific
index (within the 1-dimensional form) that corresponded to a given row and column
of a table, e.g. in the map example of Figure 2.2 such a method would
return the value 25 when its parameters were instantiated to A
and C.
In some programming languages, e.g. BASIC, it may be necessary to mimic the
structure of a multi-dimensional array by devising such indexing mechanisms to map
between the `natural' 2,3, etc dimensional structure in which a design solution
is developed, and the `linear' 1-dimensional structure of a simple array.
While this is straightforward, in an algorithmic sense, it has a number of disadvantages:
Java provides the capability to declare arrays having 2,3,4 ... dimensions using
a syntax that is similar in style to that for declaring arrays of a single dimension:
Thus if we wished to declare an array corresponding to a table of 5 rows and 4 columns,
each entry of which was an integer value, we could do this by:
This declaration can be interpreted with the array laid out as:
Figure 2.3: 2-dimensional Array (5 by 4); Indexing Row 3 and Column 1
N.B. the usage `interpreted': in terms of the actual physical storage
allocation in memory the elements would be stored in 20 consecutive
locations: the Java compiler system ensures that references to, say FiveByFour[3][1], would access
the 14th of these.
For this lecture we will consider examples involving constrained
multi-dimensional arrays, deferring the variations involving unconstrained
types until later, i.e. each dimension will have its bounds set when declared.
Consider the example declaration
In general (for a constrained array) this would look like:
[Important Note: The characters
Returning to the examples presented previously, suitable
declarations for these would be,
or, less transparently (but more efficiently in terms of the storage used),
Logically one can view,
Figure 2.4: Decomposition of 3-D Array as 2-D Arrays and 1-D Arrays
Just as an aggregate of values can be used to set the
starting values of a 1-dimensional array when it is
declared, we can also
perform the same function when instantiating an instance of a multi-dimensional array.
Thus, for the example 2-dimensional integer array, if the element FiveByFour [i][j]
should contain the value (i+1)*(j+1) to start, we could achieve this via a declaration
resulting in the following instantiation of values to elements,
Again if the road map illustration were to be instantiated when
declared, this would be done by
The layout over several lines is not essential to the correctness of such declarations,
however, it is good practice in enhancing the readability of the text. While it is
rare to have such instantiations for arrays of 3 or more dimensions, it is very
easy to make errors (e.g. assigning too few/too many elements in the definition) in
declarations involving several array dimensions, cf.
the remarks on Program Layout made
earlier (Section 9.1).
The concept of the length of a 1-dimensional is obviously
simply the number of elements it contains.
This value can be recovered by using the public final int
length that is associated with any instance of an array object, i.e.
if we use the example TenIntegerArray, introduced at the start of this
lecture, then the value of this variable is obtained by,
We have seen that a multi-dimensional array can be
regarded as a single dimensional array of arrays
of `smaller' dimension. Following this viewpoint, an instance
of a multi-dimensional array object has a length variable defined for
each distinct dimension of the array. For example,
For the 2-dimensional array FiveByFour,
For the 3-dimensional array FourByThreeByTwo,
Regarding the last point above, the manner in which say a 2-dimensional array can
have the length(s) of individual 1-dimensional arrays comprising it, constrained at
run-time, provides a number of possibilities that are not found in other languages. While
some of these may seem rather eclectic, there are applications where
significant savings in storage space can result.
Distance A B C D E F A 0 20 25 -- -- 100 B 20 0 -- 57 -- -- C 25 -- 0 92 39 81 D -- 57 92 0 -- 32 E -- -- 39 -- 0 26 F 100 -- 81 32 26 0
City-City
AA
AB
AC
AD
AE
AF
BB
BC
BD
BE
BF
CC
CD
CE
CF
DD
DE
DF
EE
EF
FF Distance
0
20
25
--
--
100
0
--
57
--
--
0
92
39
81
0
--
32
0
26
0
a b c d e f g h 8 E BP E BP E BP E BP 7 BP E BP E BP E BP E 6 E BP E BP E BP E BP 5 E E E E E E E E 4 E E E E E E E E 3 WP E WP E WP E WP E 2 E WP E WP E WP E WP 1 WP E WP E WP E WP E
O R T H O D O X Y - C O N N - E - R - D - E - H - O L A N C E - D I S D A I N Y - S - - - E - T - O - P - C I R C U S - E A S E L - - O - O - T - R - - - U L A N I M A - E D I C T S A - - - P - T - A - L - - R E E V E - H O Y D E N - A - X - L - E - - - A - G C H A B L I S - E L V E R H - C - E - I - T - E - I S E T - D E S P A I R E D
2. Multi-dimensional Array Declarations in Java
int [] [] FiveByFour = new int [5] [4];
int [] [] FiveByFour = new int [5] [4];
< Type> [] [] [] ... < NameofArray> = new < Type> [LEN1] [LEN2] [LEN3] ...;
int[][] TooFew =new int[5];
int[][] NotTooFew =new int[5][];
int [] [] RoadMap = new int [6] [6];
String [] [] DraughtsBoard = new String [8] [8];
char [] [] [] DraughtsBoard = new char [8] [8] [2];
char [] [] CrossWordGrid = new char [13] [13];
2.1. Further Attributes of Multi-dimensional Arrays
int [][] FiveByFour = { {1,2,3,4},
{2,4,6,8},
{3,6,9,12},
{4,8,12,16},
{5,10,15,20} };
final int NoRoad = Integer.MAX_VALUE; // Conventional usage.
int [] [] RoadMap = { {0,20,25,NoRoad,NoRoad,100},
{20,0,NoRoad,57,NoRoad,NoRoad},
{25,NoRoad,0,92,39,81},
{NoRoad,57,92,0,NoRoad,32},
{NoRoad,NoRoad,39,NoRoad,0,26},
{100,NoRoad,81,32,26,0}
};
int [][][] FourByThreeByTwo = { { {1,2},
{2,4},
{3,6} },
{ {2,4},
{4,8},
{6,12} },
{ {3,6},
{6,12},
{9,18} },
{ {4,8},
{8,16},
{12,24} }
};
TenIntegerArray.length
Dimension Length Value Interpretation First FiveByFour.length 5 Number of Rows Second FiveByFour[0].length 4 Number of Columns
Dimension Length Value Interpretation First FourByThreeByTwo.length 4 Number of Planes Second FourByThreeByTwo[0].length 3 Number of Rows Third FourByThreeByTwo[0][0].length 2 Number of Columns
3. Summary
(Notes prepared and maintained by Paul E. Dunne, October 1999)