-
Things to be careful of later on in this class:
-
Using equals and toString from Object to define some elements of Set
-
Reiterate over Object and subclass discussion in previous class
-
Maybe do a simple example, ...
-
Revisiting the CourseCatalog class
-
Recall the the constructor for the CourseCatalog class simply set the name
of the file
-
The file was read in each time lookup is called (and courses are constructed)
-
Vectors allow us to change this so that we only need to read the file in
once, and lookup then uses the vector.
-
In the constructor:
-
Open the file
-
Create Courses, adding them to a vector, until the file is ended
-
Close the file
-
In lookup
-
Scan through each element in the Vector, printing out those that have the
professor the same as what we're looking for.
-
Questions?
-
What should the instance variable(s) be?
-
How long do we need the file open?
-
A Set class
-
Vector is just one form of collection that is useful
-
It has an implied order (the order that the elements are added), and can
contain the same element more than once
-
A mathematical set (another form of collection), has no implied order,
and shouldn't contain the same element
-
But, a set can be implemented using a Vector
-
Set is not a predefined class - we must implement it ourselves
Determining the behavior
-
Set (constructor)
-
contains (returns true if the set contains an object)
-
isEmpty (tests for an empty set)
-
addElement (adds an element to a set)
-
copy (copies a set)
-
need copies in case there are two references to a particular set.
-
changing one would change the other...
-
size (the number of elements in the set)
-
elements (returns and Enumeration so the set can be traversed)
-
union (is the union of two sets)
-
intersection (gives the intersection of two sets)
-
print (prints out the elements in a set)
Defining the interface
Set s1 = new Set ();
Set s2 = new Set ();
s1.addElement ("A");
s1.addElement ("B");
s2.addElement ("A");
s2.addElement ("C");
s1.union (s2).print ();
s2.intersection (s1).print ();
-
The above suggests the following interface
class Set {
Set () {...}
boolean contains (Object value) {...}
boolean isEmpty () {...}
void addElement (Object value) {...}
Set copy () {...}
int size () {...}
Enumeration elements () {...}
Set union (Set s2) {...}
Set intersection (Set s3) {...}
void Print () {...}
}
-
The arguments to contains and addElement are references
to a generic Object, so that our sets can contain any object (like
a Vector)!
Defining instance variables
-
We want our set to contain an arbitrary number of elements, thus we need
a Vector.
-
This vector will be used by all of the methods, so it should be an instance
variable:
private Vector theElements;
Implementing methods
-
Pick the easy ones first!
public Set () {
theElements = new Vector ();
}
boolean isEmpty () {
return theElements.isEmpty ();
}
int size () {
return theElements.size ();
}
Enumeration elements () {
return theElements.elements ();
}
-
To copy a Set, we need to create a new set, traverse through all the elements
of the set we are copying and add them to the new set..
public Set copy () {
Set newSet = new Set (); // The set that will contain the copy
Enumeration enum = this.elements (); // The elements of this set
while (enum.hasMoreElements ()) {
newSet.addElement (enum.nextElement ());
}
return newSet;
}
-
Optional discussion questions:
-
Why use this.elements and not theElements.elements ()?
-
Note that there is an error in the text book: they interchange theElements
and vector.
-
Only want to add an element to a set if it is not already in the set (recall,
the method contains that we are yet to write checks to see if an element
is in the set).
-
Note, textbook messes this up
void addElement (Object value) {
if (!this.contains (value)) {
theElements.addElement (value);
}
}
-
To take the union of two sets, s1 and s2 we:
-
copy s1 (so we don't damage it)
-
go through all elements of s2
add it to s1 (note, adding should check whether s1 already contains
it!)
Note: the text book makes a mess of this, too.
public Set union (Set s) {
Set unionSet = this.copy (); // Copy the set
Enumeration enum = s.elements ();
while (enum.hasMoreElements ()) {
unionSet.addElement (enum.nextElement ());
}
return unionSet;
}
-
To take the intersection of s1 with s2:
-
create an empty intersection set
-
go through all elements of s1
if an element from s1 is also in s2, add it to the intersection
set
The textbook almost gets this right.
public Set intersection (Set s) {
Set intersectionSet = new Set ();
Enumeration enum = this.elements ();
while (enum.hasMoreElements ()) {
Object elem = enum.nextElement (); // Get the element
if (s.contains (elem)) { // Check to see if the element is also in s
intersectionSet.addElement (elem);
}
return intersectionSet;
}
Need elem because we use it twice
-
To see if an element is in a set, we need equality
-
If our set contains only objects, then how do we know what equals means?
-
We could use ==, but what if the users of our set want it to contain Strings?
-
Fortunately, every Object contains an equals method
-
subclasses of Object can rewrite the equals method to have a new meaning
-
this new equals will be called, whether we are viewing the element as an
Object or a String
-
(c.f., performButtonAction in the GUI classes)
-
So, we can use equals to see if an element is in the set!
public boolean contains (Object o) {
boolean isInSet = false;
Enumeration enum = this.elements ();
while (enum.hasMoreElements () && !isInSet) {
if (enum.nextElement ().equals (o)) {
isInSet = true;
}
return isInSet;
}
-
Note, this is different to the book, which returns from the loop without
it terminating
-
In my opinion, this is a bad practice, because you don't know when you
may exit the loop.
-
It's better to make the exit condition explicit!
-
To print out the set, we need some way of converting each element to a
String, in a meaningful way.
-
Again, Object helps us by defining a method toString, which can be redefined
by any subclass
public void print () {
Enumeration enum = this.elements ();
while (enum.hasMoreElements ()) {
System.out.print (enum.nextElement ().toString ());
System.out.print (" ");
}
System.out.println ();
}