11 Hashcode Map
11 Hashcode Map
1
Separate chaining
• separate chaining: Solving collisions by storing a list at each index.
add/contains/remove must traverse lists, but the lists are short
impossible to "run out" of indexes, unlike with probing
index 0 1 2 3 4 5 6 7 8 9
value
11 24 7 49
54
private class Node {
public int data;
public Node next; 14
...
}
2
Implementing HashIntSet
• Let's implement a hash set of ints using separate chaining.
public class HashIntSet implements IntSet {
// array of linked lists;
// elements[i] = front of list #i (null if empty)
private Node[] elements;
private int size;
index 0 1 2 3 4 5 6 7 8 9
value
11 54 7 49
set.add(24);
new node 24 14
4
Implementing add
public void add(int value) {
if (!contains(value)) {
int h = hash(value); // add to front
Node newNode = new Node(value); // of list #h
newNode.next = elements[h];
elements[h] = newNode;
size++;
}
}
5
The contains operation
• How do we search for an element in the hash table?
Must loop through the linked list for the appropriate hash index,
looking for the desired value.
Looping through a linked list requires a "current" node reference.
index 0 1 2 3 4 5 6 7 8 9
value
11 24 7 49
current 54
set.contains(14) // true
set.contains(84) // false 14
set.contains(53) // false
6
Implementing contains
public boolean contains(int value) {
Node current = elements[hash(value)];
while (current != null) {
if (current.data == value) {
return true;
}
current = current.next;
}
return false;
}
7
The remove operation
• How do we remove an element from the hash table?
Cases to consider: front (24), non-front (14), not found (94), null (32)
To remove a node from a linked list, you must either change the list's
front reference, or the next field of the previous node in the list.
set.remove(54);
index 0 1 2 3 4 5 6 7 8 9
value
11 24 7 49
current 54
14
8
Implementing remove
public void remove(int value) {
int h = hash(value);
if (elements[h] != null && elements[h].data == value) {
elements[h] = elements[h].next; // front case
size--;
} else {
Node current = elements[h]; // non-front case
while (current != null && current.next != null) {
if (current.next.data == value) {
current.next = current.next.next;
size--;
return;
}
current = current.next;
}
}
}
9
Rehashing w/ chaining
• Separate chaining handles rehashing similarly to linear probing.
Loop over the list in each hash bucket; re-add each element.
An optimal implementation re-uses node objects, but this is optional.
index 0 1 2 3 4 5 6 7 8 9
value
11 24 7 49
54
14
index 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
value
24 7 49 11 54
14
10
Hash set of objects
public class HashSet<E> implements Set<E> {
...
private class Node {
public E data;
public Node next;
}
}
11
The hashCode method
• All Java objects contain the following method:
public int hashCode()
Returns an integer hash code for this object.
We can call hashCode on any object to find its preferred index.
HashSet, HashMap, and the other built-in "hash" collections call
hashCode internally on their elements to store the data.
12
Issues with generics
• You must make an unusual cast on your array of generic nodes:
public class HashSet<E> implements Set<E> {
private Node[] elements;
...
public HashSet() {
elements = (Node[]) new HashSet.Node[10];
}
14
Good hashCode behavior
• A well-written hashCode method has:
Consistently with itself (must produce same results on each call):
o.hashCode() == o.hashCode(), if o's state doesn't change
15
Example: String hashCode
• The hashCode function inside a String object looks like this:
public int hashCode() {
int hash = 0;
for (int i = 0; i < this.length(); i++) {
hash = 31 * hash + this.charAt(i);
}
return hash;
}
16
hashCode tricks
• If one of your object's fields is an object, call its hashCode:
public int hashCode() { // Student
return 531 * firstName.hashCode() + ...;
17
Implementing a hash map
• A hash map is like a set where the nodes store key/value pairs:
public class HashMap<K, V> implements Map<K, V> {
...
}
index 0 1 2 3 4 5 6 7 8 9
value
// key value
map.put("Marty", 14); "Kasey" 20
map.put("Jeff", 21);
map.put("Kasey", 20);
map.put("Stef", 35);
19
Hash map vs. hash set
The hashing is always done on the keys, not the values.
The contains method is now containsKey; there and in
remove, you search for a node whose key matches a given key.
The add method is now put; if the given key is already there, you
must replace its old value with the new one.
• map.put("Bill", 66); // replace 49 with 66
index 0 1 2 3 4 5 6 7 8 9
value
"Abby" 57 "Kasey" 20
"Bill" 49 66
20