CSE 283 Introduction to Object Oriented Design

Barbara Nostrand, Ph.D.

Building Objects Containing Objects


Arrays are linear structures similar to a row of boxes at the post office.  Each cell in an array has a unique number,  called an index.  These indices have strictly integer values and are assigned sequentially beginning with zero.  Thus the first value stored in an array is numbered 0,  the second is numbered 1,  the third 2,  and so forth.  In C,  C++,  and Java,  arrays have only one dimension.  Some languages such as Fortran and Basic natively support multi-dimensional arrays such as matrices which are two dimensional structures.  However,  C,  C++,  and Java represent a 2-dimensional matrix as an array of arrays.  This idea can be generalized to higher dimensions and so matrix[3][4][5] will return the sixth element In the fifth array in the fourth array of the array called matrix.  in C and C++,  arrays are simply contiguous segments of memory referenced by a pointer.  C and C++ allow you to statically create arrays pretty much at compile time: 

    int integerArray[3][5];        // Creates a static integer array in C/C++
allocates 3*5*integerWidth bytes of memory.  This allocation is fixed at compile time.  The only way to fix the size of the array at run time instead of compile time is to use the malloc or calloc functions to dynamically allocate memory for an array.  This dynamically allocated memory is released by invoking the free function.  Although declaring arrays inside functions in C and C++ does allocate memory from the system stack instead of the heap the size of these arrays is still fixed at compile time.  Finally,  arrays in C and C++ are interchangeable with pointers.  This is not true with Java where arrays are a specialized kind of object. 

Since arrays in Java are objects and not pointers,  they have attributes.  The public attribute length holds the number of cells in the array.  Like C and C++,  once an array is created,  its length can not be changed.  While arrays are objects in Java,  the Array class is somewhat different from most other classes.  For example,  some operations on arrays have specialized syntax.  However,  since Java arrays are objects they are always created dynamically and are automatically garbage collected (similar to being freed) when they are no longer in use. 


Before considering arrays further,  let's consider strings again.  We have been using strings since the begining of this course.  Internally,  strings contain an array of characters.  Since the length of an array is fixed once it is created,  strings pretty much lack mutator methods.  Instead new strings are created as needed.  An interesting difference between strings in C/C++ and Java results from arrays being objects in Java.  In C/C++,  the end of a string must be marked by using a nil (a binary zero) as an end of string marker which can occur anytime before the end of the character array.  Also,  since C/C++ strings are simply character arrays,  their contents can be modified.  Java strings are a bit different in that the underlying character array has a knowable length and can not be modified.  Ordinarily,  we would have to use constructor and accessor methods for arrays to manipulate strings.  Fortunately,  Java implements special syntax for strings.  In particular,  even though the array inside a praticular string is immutable,  Java implements + as a concatenation operator.  Further,  Java also overloads the relational operators so that they apply to strings as well as numbers.  Consequently,  the following code fragment will print true

    String a = "a";
    System.out.println(a == "a");
Regardless,  strings are still objects and have associated methods for manipulating them.  You should use the equals(Object string) and equalsIgnoreCase(Object string) methods when comparing strings. 

Building an Array

Like the String class,  arrays have special syntax for creating them.  You are already familiar with objects which contain primitive data types.  For example,  you are familiar with arrays of integers.  First,  we need to declare an array variable to keep track of our array: 

    int [] array;
This creates a variable to keep track of an array of integers,  but it does not actually create an array of integers.  This is quite different than what happens with C/C++ in the examples above.  In Java,  you do not specify the length of the array when you declare an array variable.  Also,  remember that Java has strong typing which means that our variable array is specialized to keeping track of an array of integers.  JVM will object to trying to store something else in array.  You actually construct an array of integers by applying the keyword new to a complete specification of the array being created.  For example: 
    new int[20];
This actually creates an array of twenty integers.  However,  it does not assign it to a variable.  This means that the array will be immediately garbage collected.  You can create an array and and assign it to a variable in any of several ways: 
    int [] array;
    array = new int[20];
This first creates a variable to hold an integer array,  and then creates an array of 20 integers and links the array variable to the newly created array.  You can do both operations with a single statement: 
    int [] array = new int[20];

Initializing an Array

In the examples given above,  we have created arrays of 20 integers and used the integer array variable array to hold on to it.  However,  we have not initialized these integers to any particular value.  Most systems will automatically initialize values to a binary zero.  This will be interpretted as a null for object values.  You can initialize arrays in either of two ways.  You can either load a value into each cell of the array individually,  or you can load the array with an array literal.  For example: 

    int [] array = {5, 4, 3, 2, 1};
which creates an array of five integers.  This syntax also works for objects.  For example,  we can create an array of strings: 
    String[] array = {"Huey",  "Dewey",  "Loui");
which creates an array of three strings and attaches a cell to each of these strings.  You can also create a string array as follows: 
    String[] array = new String[20];
which creates an array of 20 strings.  But,  none of the cells actually has a string in it!  Instead,  each of them has a null in it.  This means that attempting to perform a string operation on array[0] will generate a null pointer exception

Initializing an Array of Structures

You should now guess that you need to fill an array of objects with objects before you can use it.  Further,  these objects must belong to a class which actually instantiates objects.  they can not belong to either an interface or an abstract class.  For example,  you can create an array of stacks in the following way: 

    MyStack<String> = new MyStack<String>[20];
Note that we are instantiating a class called MyStack which needs to impliment the Stack interface.  Although we have succeeded in creating an array for objects which implement the Stack interface,  no stacks have been created to populate this array!  To populate the array with stacks you must explicitly create each of the stacks and place it into its cell in the array.  For example: 
    for(int i = 0; i < array.length; i++) array[i] = new MyStack<String>();
which assigns each cell of array to a unique empty stack of strings.  You need to explicitly construct each of these stacks.  If instead,  you execute the following procedure: 
    MyStack<String> temp = new MyStack<String>();
    for(int i = 0; i < array.length; i++) array[i] = temp;
You will actually create an array of some twenty cells all linked to the very same stack of strings!  Also,  please note that we are using the hypothetical class MyStack which we are presuming to be non-abstract and not Stack which is actually an interface and not a class

You can sometimes avoid this two step technique by initializing your array variable to a literal array of stacks: 
    MyStack<String>[] array = {new MyStack<String>(), new MyStack<String>(), new MyStack<String>()};
which creates an array of three empty stacks of strings. 

Library Methods for Arrays

Java associates two utility classes with arrays.  The Array class contains basic static methods for manipulating arrays,  while the Arrays class contains a number of advanced static methods such as sorting and searchingArrays is part of the Java Collections Framework.  This is a unified architecture for representing and manipulating collections,  allowing them to be manipulated independently of the details of their representation.  A collection,  sometimes called a container,  is simply an object that groups multiple elements into a single unit.  Collections are used to store,  retrieve,  manipulate,  and communicate bundles of data.  Typically,  they represent data items that form a natural group,  such as a poker hand (a collection of cards),  a mail folder (a collection of letters),  or a telephone directory (a mapping of names to phone numbers).  Some collections such as:  Vector,  Hashtable,  and array,  may already be familiar to you from other languages such as C and C++. 


Vectors are essentially variable length arrays.  This construct does not exist in C,  but does exist in C++ and Java.  While C++ arrays are simply pointers to statically allocated regions of memory just like C,  the vectors of both C++ and Java are objects.  So while there is a significant difference between the arrays of C++ and Java,  the vectors of C++ and Java are pretty similar.  Further,  unlike arrays Java vectors do not have a special syntax,  and instead use more typical accessor and mutator methods. 

Other Complex Structures

Creating other complex structures containing objects follows the same pattern we saw above for strings.  You can build a list of stacks of strings or once you have defined an automobile class,  you can build a list of stacks of arrays of automobiles.  However,  you need to make sure that you correctly populate these structures,  otherwise your software will throw a NullPointerException when you attempt to access part of your data structure. 

Cloning Objects

Recall that assignment statements on object variables results in copying the link to a shared object and not the object itself.  This means that the very same object is being referenced by two variables and that invoking methods attached to one variable will be reflected in the shared object and be visible from the second variable.  Wrapper classes are protected from this by not having mutator methods.  However,  let's think about how this might work with the wrapper class Integer if it had a mutator method int value(int integer)

    Integer fred = new Integer(3);
    Integer tom = new Integer(5);
    tom = fred;
This code would,  print out 10 and not either 3 or 5.  Consequently,  we need to use a clone() method to obtain a new object with the same internal values as the original.  Some classes in the Java Class Library implement the cloneable interface.  This interface requires specification of a clone() method which makes such a copy.  Finally,  when using a cloneable class you need to be careful to check whether the clone operation produces a shallow copy or a deep copy.  A shallow copy copies only the top level structure leaving all lower-level links unaffected.  A deep copy unravels the entire structure down to primitives copying all components.  As you can no doubt guess,  deep copying is much more expensive than shallow copying.  Consequently,  shallow copying is the prefered default.  However,  you will need to perform a deeper copy if you wish to isolate the objects in the two structures from each other.  Traditionally,  each class in the object must be individually inspected and edited to implement the Cloneable interface and override its clone() method in order to make a deep copy of itself as well as its contained objects.  Carefully consider the following simple example which illustrates the difference between shallow and deep cloning

Original Object

After a Shallow Copy

Original Object

After a Deep Copy

Last modified: 2007 NOV 05