We had a need for hash tables (or hash maps) when looking up move
functions in
framework.problem.Mover.
Now we need hash tables to implement graph representations, and we will
identify more uses later.
This presentation discusses how hash tables are implemented and how
they achieve their lookup efficiency.
A
hash table is an efficient data structure for
implementing
dictionary operations.
Dictionary operations (
search, insert, remove) are required
for
dynamic sets.
Java's collections framework has several classes that implement hash
tables, e.g.
Hashtable and
HashMap, both in
the
java.util package.
These classes implement the general interface
Map<K,V>,
also in
java.util. A map is simply an object that maps keys to
values.
Hashtable and
HashMap both achieve constant-time lookup
provided that the hash function used achieves simple uniform hashing.
The keys used in a map can be any Java object whatever; when Java
hashes such a key object, it uses the object's
hashCode
method.
As we've seen, Java's
Object class defines the
hashCode
method that is used to look up objects on
HashMaps
and
Hashtables.
For keys that are instances of Java
library classes, the provided
hashCode method is fine. For
example:
- For integers, use the provided Integer.hashCode
method
- For strings, use the provided String.hashCode
method
For keys that are instances of classes that you define, however, in
most cases you will need to provide a
hashCode method of your
own.
All classes inherit the
Object.hashCode method, but that method
simply hashes an object's address.
For most cases, however, this is not adequate, because:
Java Object Hashing Rule: If two objects are equal by a
class's equals method, then the hash codes of the two objects
must be the same.
For example, if we have two
PuzzleState objects, and the objects
satisfy
equals, then if we have not overridden
the
hashCode method the above rule will be violated because the
two objects' addresses will hash to different values.
Below is one way to write
hashCode for
PuzzleState that
satisfies the rule.
It manually processes the tiles using nested loops:
Another way is to use Java's library methods to accomplish the same
purpose:
This method was generated automatically by NetBeans.
Hashtable and
HashMap both implement hash tables by
hashing keys using the
hashCode method.
If the hash table does not have to be thread-safe,
HashMap is
preferred.
We will use a HashMap to implement a graph's adjacency list
representation.
We need to efficiently look up a vertex's adjacency list, so we will
use a vertex as a key and map it to a list of vertices as the value.
Later we will define the
Vertex class in a Java graph
framework.
Here is how the adjacency list representation as a HashMap is declared
and initialized:
You can determine if a HashMap already contains a key by using
the
containsKey method.
In our case, if a vertex is not already a key, we need to map it to a
new empty list of vertices using the
put method:
Suppose we want to add the edge
(v1,v2) to a graph. First we
retrieve
v1's adjacency list using the
get method:
Then we can add
v2 to the list:
If we want to check whether there already is an edge between
v1
and
v2: