In this homework, you will implement a hash dictionary (also known as a hash map) and a hash set. We will be using these two data structures extensively in the next project.
This entire project is due on Friday February 8 Tuesday February 12 at 11:59pm.
You will use these files from your prior assignments
main.java.datastructures.concrete.dictionaries.ArrayDictionary.java
main.java.datastructures.concrete.DoubleLinkedList.java
If you have chosen a new partner for this assignment, choose either of your submissions from HW2 and verify that these are functioning properly.
You will be modifying the following files:
main.java.datastructures.concrete.dictionaries.ArrayDictionary.java
main.java.datastructures.concrete.ChainedHashSet.java
main.java.datastructures.concrete.dictionaries.ChainedHashDictionary.java
Additionally, here are a few more files that you might want to review while completing the assignment (note that this is just a starting point, not necessarily an exhaustive list):
test.java.datastructures.TestChainedHashSet.java
test.java.datastructures.dictionaries.TestChainedHashDictionary
test.java.datastructures.dictionaries.TestArrayDictionary.java
main.java.datastructures.interfaces.IDictionary.java
main.java.datastructures.interfaces.ISet.java
Here's another video overview.
Here are some baseline expectations we expect you to meet in all projects:
Follow the course collaboration policies
DO NOT use any classes from
java.util.*
. There are only two exceptions to this rule:
You may import and use java.util.Iterator
and
java.util.NoSuchElementException
.
You may import and use anything from java.util.*
within your testing code.
DO NOT make modifications to instructor-provided code (unless told otherwise). If you need to temporarily change our code for debugging, make sure to change it back afterwards.
Clone the starter code from GitLab and open the project in your IDE. See the instructions from HW0 if you need a reminder on how to do this.
Copy your DoubleLinkedList.java
and ArrayDictionary.java
files from HW2 to this new one.
The copied code will not immediately compile. This
is ok: we modified IDictionary
so it requires
you to implement one additional method.
Copy your DoubleLinkedList
tests from HW2. You may replace the file
provided with the one from HW2, since the one included in HW3 has some outdated test
names. Make sure to also copy over the delete tests you wrote from
TestDeleteFunctionality.java
and TestDeleteStress.java
to the
new TestDoubleLinkedList.java
.
Finally, make sure everything works.
Try running TestDoubleLinkedList
and make sure the tests run.
Try running SanityCheck.java
, and try running Checkstyle.
Checkstyle should still report the same 5 errors with SanityCheck.java
as it did with HW2.
ArrayDictionary
Task: implement ArrayDictionary.iterator()
.
Take a look at IDictionary
. You may notice that the
interface now requires all subclasses to implement one additional
method: iterator()
.
By implementing this method, we can now (efficiently) iterate over all key-value pairs in our dictionaries! For example, we can now do this:
IDictionary<String, Integer> foo = makeTheDictionary();
for (KVPair<String, Integer> pair : foo) {
String key = pair.getKey();
Integer value = pair.getValue();
// Do something with key and value
System.out.println(key + " : " + value);
}
The above program should print out every key-value pair contained within
foo
.
Notes:
Your iterator()
method may return the key value pairs
in any order you wish. The easiest approach would likely be to
return them in the order your pairs are listed in your internal
array.
You may assume the user will not modify the dictionary while you're iterating over it.
You may NOT create any temporary data
structures such as a temp IList
when implementing
your iterator. We want our iterators to be efficient, and having
to copy the contents of our dictionary to some other data
structure at any point is suboptimal.
You may, however, create new KVPair
objects. (They
do not count as "temporary" data structures).
We have provided you with unit tests for your iterators. You can add more tests if you want.
If you need examples on how to implement an iterator, see
DoubleLinkedList
and the stubs in
ChainedHashDictionary
. Your iterator will look something like this:
private static class ArrayDictionaryIterator<K, V> implements Iterator<KVPair<K, V>> {
// Add any fields you need to store state information
public ArrayDictionaryIterator(/* possible constructor arguments */) {
// Initialize the iterator
}
public boolean hasNext() {
// Implement hasNext
throw new NotYetImplementedException();
}
public KVPair<K, V> next() {
// Return the next KVPair in the dictionary
throw new NotYetImplementedException();
}
}
Make sure to make the class private and static, and make sure you copy how the generics are being defined correctly.
The IDictionary
interface requires that dictionaries
return KVPair
objects. However, your internal dictionary
uses private Pair
objects. You will need to convert
between the two within your iterator—see the method header comments
in KVPair
to understand how to instantiate new
KVPair
objects.
ChainedHashDictionary
Task: complete the ChainedHashDictionary
class.
In this task, you will implement a hash table that uses separate chaining as its collision resolution strategy.
Notes:
ArrayDictionary
for your internal chains/buckets.iterator()
method doesn't need to yield the
pairs in any particular order.Correctly implementing your iterator will be tricky—don't leave it to the last minute!
Try to finish the other methods in ChainedHashDictionary
as soon as possible so
you can move on to implementing iterator()
.
The comments in ChainedHashDictionary
will contain a few hints on how to
implement this method.
In the class when we covered separate chaining hash tables we used LinkedList
as
the chaining data structure. In this task, instead of LinkedList
, you will use your
ArrayDictionary
(from HW2) as the chaining data structure.
When you first create your chains
array, it will contain null pointers. As
key-value pairs are inserted in the table, you need to create the chains
(ArrayDictionary
s) as required. Let's say you created an array of size 5 (you can
create array of any size), and you inserted the key-value pair ("a", 11).
Dictionary<String, Integer> map = new ChainedHashDictionary<>();
map.put("a", 11);
Your hash table should something like the following figure. In this example, the key "a"
lands in index 2, but if might be in a different index depending on your table size. Also, in
this example, ArrayDictionary
(chain) is of size 3, but you can choose a different
size for your ArrayDictionary
.
Now, suppose you inserted a few more keys:
map.put("f", 13);
map.put("c", 12);
Your internal hash table should now look like the figure below. In this example, keys "a" and "f" both hash to the same index (2).
Hash function: Do not try to implement your own hash function. Use the hash
method hashCode()
that Java provides for all classes: so to get the hash of a key,
use keyHash = key.hashCode()
. This method returns an integer, which can be
negative or greater than chains.length
. How would you handle this?
Resizing strategy: Recall that operations on a hash table slow down as the
load factor increases. So you need to decide when to resize (increase) your internal array, by
how much, and actually do the resizing. When resizing your ArrayDictionary
, you
just copied over item from the old array to the new one. Here, how would you move items from one
hash table to another?
ChainedHashSet
Task: complete the ChainedHashSet
class.
In section c, you implemented the dictionary ADT with a hash table. You can also implement
the set ADT using hash tables. Recall that in sets, the data item to store is just a key, not a
key-value pair. In sets, the operation of interest is contains(key)
, which returns
whether a key is part of the set. Hash tables provide an efficient implementation for this
operation.
Notes:
IDictionary<KeyType, Boolean>
to store items (keys) in your
HashSet
. Since there are no key-values pairs (only keys) in sets, we will
completely ignore the values in your dictionary: use a placeholder boolean whenever
necessary.Task: submit answers to the following questions.
Each group member should answer and submit these questions
independently. These are graded credit/no credit, but we still expect some level of detail
regarding your experience.
Submit your individual writeups (in plaintext .txt
) at
this Canvas page.
Use this template to write your individual writeup.
The following deliverables are due on Friday February 8 Tuesday February 12 at 11:59pm.
Before submitting, be sure to double-check and make sure that...
ArrayDictionary
class is fully repaired and
passes all tests.
ChainedHashDictionary
class is fully
implemented and passes all tests.ChainedHashSet
class is fully
implemented and passes all tests.Submit by pushing your code to GitLab. If you intend to submit late, fill out this late submission form when you submit.
Both partners should turn in their individual writeup (section e) by the deadline via Canvas at this page.