Lecture week 19 - Collection classes in Java
It is frequently required in programs to deal with large numbers of similar
things, e.g. :-
- A set of student marks - an array of ints.
- A variable sized collection of Turtles - a Vector of Turtles.
- A set of customers - an array of Customers.
As it is difficult and inconvenient to have to give each data item its own
variable name, the whole collection is given a name and the items within
the collection referred to by a subscript or some other key value.
JDK1.1 provides three main collection classes as follows :-
- Arrays - a fixed size collection of things referenced by an integer
subscript. These are similar to arrays found in virtually all programming languages.
- Vectors - similar to arrays but of variable size. These can grow and
shrink as the need requires.
- HashTables - A kind of dictionary in which items are located by
some key value. These will be considered later in the course, but are
probably the most useful and powerful forms of collection. They, like Vectors,
can grow and shrink on demand.
Type compatibility extensions
Before considering Vectors, there is an important issue to raise
concerning type compatibility.
As you will remember Java is a strongly typed language - that is, you are
not allowed to mix types in expressions or when making assignments, e.g.
int j = 10;
float x = 23.4;
j = x; //illegal as the 0.4 will be lost leaving 23
x = j; //legal as no information is lost
j = (int)x; //legal as programmer instructs computer to do it
Paper p = new Paper();
Turtle t = new Turtle(p);
p = t; //illegal as a Turtle is a different type to Paper
From this you will see that with built-in types - byter, short, int, long,
float, double, char, boolean conversion is only automatic if no information
is lost, otherwise it must be explicitly requested with a cast such as
(int).
In the case of variables referencing objects, the situation isdifferent. Any
variable of a particular class can be assigned a variable of the same class
OR ANY SUB CLASS. Hence the following is legal :-
Paper pad = new Paper();
Turtle t;
GraphicsTurtle g = new GraphicsTurtle(pad);
t = g; //legal as g is a sub-class of t
g = t; //illegal as t is a super-class of g
This ability to assign a sub-class to a super-class variable also applies
to method parameters, e.g. given a method :-
public void demoMethod(Turtle t) {
//code to manipulate a Turtle
}
another program can call this method, passing it not only a Turtle instance
but also any object which is a sub-class of Turtle, e.g. :-
Paper p = new Paper();
GraphicsTurtle gt = new GraphicsTurtle(p);
someObject.demoMethod(gt); //pass a GraphicsTurtle instead of a Turtle
This turns out to be a very powerful feature of object-oriented languages.
Making collection classes more useful
Collection classes like Vectors when defined have to be declared as
being collections of some kind of object. In the case of Vectors this
is predefined as instances of the class Object.
The above rule governing type compatibility will allow us to place into a
collection any instance of the class Object or any of its sub-classes.
As all classes are sub-classes of Object this means a collection can
contain ANY class of object at all - and at the same time if desired.
The need for wrapper classes
Note in the above description, a Vector may contain any class of object -
the problem with this is that if we want to store simple ints for
example we cant. They are not objects (i.e. they do not belong to a class
which is a sub-class of Object).
The problem is solved by the provision in Java of what are called wrapper
classes. If you want to store ints in a Vector then you must
create an instance of the class Integer which contains the int
as an attribute and then this can be placed in a Vector. The int
value can later be retrieved from inside the Integer instance object.
Wrapper classes exist for all the main categories of built-in types -
Integer, Float, Character, Boolean.
The need for downcasting
One problem of using Vectors is that, because Java thinks that a Vector is
a collection of Object class instances, when you retrieve an item
from the collection, the compiler assumes that it is an instance of the
class Object.
If you know that it is actually a Turtle instance and want to draw
with it by sending messages like penDown() and moveTo() the
compiler will object because these methods are not defined for instances
of the class Object.
In order to allow these messages, or for that matter the assignment of the
item to a reference of class Turtle you have to supply a cast
to inform the compiler that the item being retrieved really is a Turtle.
This is done like this :-
Turtle t;
Vector v = new Vector();
//code to put various things into the Vector v
t = (Turtle)v.elementAt(3); //access the fourth item in the vector
//and assign it to the variable t
//inform the compiler that it is a Turtle
t.penDown(); //send it messages as usual
//other commands to draw whatever
The process of casting from the presumed class - Object to a sub-class
Turtle is called downcasting. This is a dangerous process as it
relies on our knowledge of what the vector contains. If the fourth item was
not actually a Turtle but say an Integer the messages subsequently
sent like penDown() would be meaningless and would cause the program
to crash.
Vectors in Java
The Vector class in Java provides an extensible array of any kind of object
and supports a number of powerful methods such as :-
- void addElement(Object item) - adds item to end of Vector.
- Object elementAt(int index) - returns the Object at position index.
- int size() - returns the size of the collection - number of items.