The goals of this lab are
As mentioned in the pre-lab reading, the goal of this lab is to practice writing generic classes, and secondarily to practice writing nested classes. Many parts of this will be similar to Project 3, so the emphasis in this description will be on what is different.
As usual, make a directory for this lab. Copy the following code from the public directory.
cp -r /homes/tvandrun/Public/cs245/lab8/. .
Open Eclipse and make a new project from the existing source in your directory.
Complete the following steps in order, making sure that the appropriate JUnit tests pass at each step.
In the class ComparatorFactory
,
you'll see a stub for a method lexStringComparator
that returns a comparator for
strings.
The class LexComparatorTest
is a JUnit test
that tests the comparators returned by that method.
Finish the lexStringComparator
method
(just change one line)
so that it returns a comparator that compares strings lexicographically
(hint: that's the same way that String.compareTo()
would compare them).
Make sure that all the tests in LexComparatorTest
pass.
First, try running the JUnit test suit SLMStringTest
.
Three test, emptyContainsKey
, emptyRemove
,
and emptyGet
should pass out of the box "by luck".
Open the class SortedListMap
.
You can see the given code, including dummy methods and method stubs.
There is also a stub for a Node class and an instance variable
for the head node.
The node class is a non-static member class, so it is
implicitly generic since SortedListMap
is generic.
Your present task is
Node
class.
Unfortunately no new JUnit tests will pass after this, but make sure that the ones that were passing before still pass.
Write the method findNode()
that will
return the node associated with the given key, or
null if none exists.
Use the comparator to compare keys.
This will make putContainsKey
,
putGet
, and putReplace
pass.
Write an iterator for this that will return the keys. Do this as an anonymous inner class. Make every effort to do this without looking at in-class examples.
This should make emptyIterator
,
and populatedIteratorIgnoreOrder
pass.
Now the really interesting one. You were given a put method, but it doesn't work right. Re-write this method so that it overwrites the association for the given key (if it already exists), or puts a new association in the right order using the comparator otherwise.
In recent semesters I've been surprised how many students have trouble with this one. Think through it carefully---don't just try to hack up some code, actually think through the cases you would need to handle. Don't code until you can articulate the task that needs to be done.
Doing this correctly will make the test populatedIterator
pass.
Now write the remove()
method.
Since this will be almost verbatim from what you did in project 3,
I will give you most of the code for this, for the sake of time.
You need to (a) talk through this with your partner and make sure you understand
every line, and (b) fix the missing pieces
(do not use ==
or equals()
,
and make sure you understand why not):
public void remove(K key) { if (head == null) return; else if (/* some condition */) head = head.next; else { Node current = head; while (current.next != null && /* some other condition */) current = current.next; if (current.next != null) current.next = current.next.next; } }
All JUnit tests in SLMStringTest
should now pass;
newly passed tests are populatedRemove
and
removeIterator
.
The advantage comparators have over comparables is that
since one can write many comparators for a class, one can have
many ways to order a class.
In ComparatorFactory
finish the method
consonantsStringComparator
which compares two strings alphabetically, case-insensitive, looking only
at the consonants.
For example, Alice
and Luca
would be considered equivalent because they have the same consonants,
ignoring case.
Likewise blaaaaaaaaah
and blah
would be considered equivalent,
and Azekah
would come after Ziba
.
Consider y
to be a consonant.
This will take a bit of problem-solving, and you may want to
make use of methods from
Java's String class.
I can think of two approaches:
one makes use of String
methods
toLowerCase()
, indexOf()
, and of course
charAt()
and length()
;
the other approach makes use of toLowerCase()
,
split()
and compareTo()
.
When you have written this, test the comparator directly with
ConsComparatorTest
.
Then make sure that your SortedListMap
works just
as well with this comparator as with the other
by running SLMConStringTest
, which is
very much like SLMStringTest
except it uses the
new comparator.
Take a look at SLMDriver
.
This program uses SortedListMap
to associate
student records with grade records.
Look also at StudentRecord
and GradeRecord
,
which are pretty simple classes.
SortedListMap
prints a grade report
for the students in the gradebook
map using
one of three comparators for StudentRecords
.
Write these three comparators as anonymous classes in
ComparatorFactory
.
There are no JUnit tests for these;
instead, test each one by commenting out and uncommenting the appropriate
lines in SLMDriver
and observing the results.
byNameComparator
should order
StudentRecord
s primarily by last name (lexicographically),
secondarily by first name.
What this means is that if two people have the same last name,
then the comparator reverts to looking at the first name to disambiguate:
Janet Fitzgerald would come before Joey Fitzgerald.
byIdComparator
should order
StudentRecord
s by their student ID.
(Hint: you can write this with a one-line compare()
method.
Hint hint: Thin about how you can use subtraction to
produce the right result.)
byCPOComparator
should order
StudentRecord
s primarily by
CPO and secondarily by ID.
(Suppose that because of space constraints, some students share
a mailbox.)