Arrays

Collection classes

Since most programs involve gathering, manipulating and presenting information, it is important to know ways to organize the data objects we have. Java supports a number of different container mechanisms. Classes are the simplest kind of container. Each instance of a class can hold a variety of data values and methods to act on them. Arrays are the simplest container that holds more than one object. It features fast access, but it is fixed in length and is slow to insert or delete elements.

Arrays

Arrays are the oldest and simplest kind of container. An array is a set of variables, placed sequentially in memory. All the variables are of the same type. Once an array is defined, it doesn't get any longer or shorter. You can make arrays of any type in Java. The individual variables in the set are accesses by using their index. Array indices in Java start at 0. So a 5 element array has elements numbered from 0 to 4.

Arrays are declared using this syntax.

type[] variable_name;

As in other kinds of objects, this doesn't actually create the array, this makes a reference. So to make a 5 element integer array, we do
int[] foo = new int[5];

This creates a block of memory big enough to hold 5 integers. It would look like this:
Index 0 1 2 3 4
Elements          
Note that there are no data values in the elements. The arrays is not initialized. The object that represents the array has a length field in it that holds the number of elements in the array.

To use an individual element of the array, we use subscript notation. This just involves putting an index between two square brackets after the array name. Here are some examples.


int x = foo[2];
foo[3]=9;
int y = foo[x];
for(int i = 0; i< foo.length; i++)
		foo[i] += 6; 

Java arrays are automatically perform bounds checking. This means that every time you reference an array element, the system checks that you are not outside the bounds of the array. The bounds are indices between 0 and the length-1 of the array. In our example array, references like foo[-1] or foo[10] would cause an exception.

Java supports multi-dimensional arrays. The example above is a one-dimensional array. A table is an example of a 2D array. Each row in the array will usually have the same number of columns. This is not required because Java represents 2D (and higher) arrays as an array of arrays. Remember that arrays are objects and you can make an array of any object. So you create an array where every element is an array object. Since the array object is a reference to an array, each one could be a different length.

To create a 2D array, you declare it much like the 1D example.


int[][] foo = new int[3][4];
This creates a 2D array of 3 rows and 4 columns. Here are some examples of using a 2D array.

int x = foo[2][1];
foo[3][0]=9;
int y = foo[x][0];
for(int i = 0; i< foo.length; i++)
		for(int j = 0; j< foo[i].length; j++)
				foo[i][j] += 6; 

You can go to higher dimensionality. Just keep adding pairs of square brackets. A use for this would be to represent data that is organized along several axes. If you recorded the temperature at a number of different locations, you could have a 4D array of data. Something like data[latitude][longitude][altitude][temperature].

Array odds and ends

Arrays can be initialized. A 1D array of integers could be initialized like this.

int[] ages={12,47,45,9,3,3};
The data inside the braces is called an initializer. This syntax can only be used when an array is first declared. Also note that there is no call to the new operator. Initializers can be used with multi-dimensional arrays. You simply nest them. For example,

			int[][] foo = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };
Each of the nested initializers represents one row in the array.

Arrays can be passed as parameters. Since the array name is a reference to the array, what is passed is not a copy of the array, but a copy of the reference. This means that if you alter an array element inside a method, you are changing the value in the original array.

Arrays of objects

When creating an array of objects (class instances rather than primitive types like integers), the creation of the array and the creation of the objects are two separate things. Here is a short example

public class objarray
{
   public static void main(String[] args)
   {
       String[] foo = new String[10];  // make 10 String objects
       System.out.println(foo[1].length());
    } // main
} // objarray
This compiles correctly but when it is run, we get

>>javac objarray.java
>>java objarray
Exception in thread "main" java.lang.NullPointerException
        at objarray.main(objarray.java:6)

This is because while we have created 10 String references, we haven't create any Strings. Let's create some strings and try again.

public class objarray
{
   public static void main(String[] args)
   {
      String[] foo = new String[10];  // make 10 String objects
      for(int i=0; i < foo.length;i++) {
         foo[i] = new String(Integer.toString(i*2));
      } // for
      for(int i=0; i < foo.length;i++) {
         System.out.println("foo[" + i + "](" + foo[i] + 
                            ") length = " + foo[i].length());
      } // for
   } // main
} // objarray
The output from this is

>>javac objarray.java
>>java objarray
foo[0](0) length = 1
foo[1](2) length = 1
foo[2](4) length = 1
foo[3](6) length = 1
foo[4](8) length = 1
foo[5](10) length = 2
foo[6](12) length = 2
foo[7](14) length = 2
foo[8](16) length = 2
foo[9](18) length = 2

Array details

This is a bit about how arrays are implemented and used at a low level. The type of the elements is know as the base type. The objects in an array can be of any type as long as they are all the same. The size of an array is fixed at compile time. Since the elements are contiguous, we can directly calculate the address of any elements given three pieces of information.

Addressing

We need the size of the elements, the address of the first element and the number of the element in the array. The expression for doing this is:

initial_address + (element#) * size_of_base_type
For example, if we had an array of integers (each four bytes long) that started at address 15, the location in memory (the address) of element 10 is

15 + (10) * 4  =  55 
Arrays in Java start with an element number of 0. This is important to remember as people usually start counting at 1.

Multi-dimensional Arrays

Multi-dimensional arrays are stored as sequences of one dimensional arrays. An NxM array has N rows and M columns. Either each row is stored (row major) or each column (column major). The calculation above is extended again to first find the row we want by changing element# to row# and size_of_object to size_of_row. This gives us the initial address of the row we want and we use exactly the first version of the expression to calculate the column address. First the row address
initial_address + (row# - initial_row#) * size_of_row
Then find the column within that row

row_address + (element# - initial_element#) * size_of_base_type
Arrays are characterized by fast random access. Any element on the array can be found as fast as any other. They are however, limited to the size originally allocated and all the elements must be the same size. It is also difficult to insert or delete elements.