Skip to content

Commit

Permalink
Hashlab
Browse files Browse the repository at this point in the history
  • Loading branch information
greenber committed Apr 17, 2014
1 parent f97e73d commit 3d9a51b
Show file tree
Hide file tree
Showing 10 changed files with 1,248 additions and 0 deletions.
7 changes: 7 additions & 0 deletions hashtables/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
<classpathentry kind="output" path="bin"/>
</classpath>
7 changes: 7 additions & 0 deletions hashtables/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
*.class

# Package Files #
*.jar
*.war
*.ear
/bin
17 changes: 17 additions & 0 deletions hashtables/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Hash Tables</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
674 changes: 674 additions & 0 deletions hashtables/LICENSE

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions hashtables/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
hashtables
==========

A simple and partial implementation of hash tables.
360 changes: 360 additions & 0 deletions hashtables/src/taojava/util/HashTable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,360 @@
package taojava.util;

import java.io.PrintWriter;

import java.util.Iterator;
import java.util.Random;

/**
* A simple implementation of hash tables.
*
* @author Samuel A. Rebelsky
*/
public class HashTable<K, V>
implements Iterable<V>
{
// +-------+-----------------------------------------------------------
// | Notes |
// +-------+

/*
We use linear probing to handle collisions. (Well, we *will* use
linear probing, once the table is finished.)
We expand the hash table when the load factor is greater than
LOAD_FACTOR (see constants below).
Since some combinations of data and hash function may lead to
a situation in which we get a surprising relationship between values
(e.g., all the hash values are 0 mod 32), when expanding the hash
table, we incorporate a random number. (Is this likely to make a
big difference? Who knows. But it's likely to be fun.)
For experimentation and such, we allow the client to supply a
Reporter that is used to report behind-the-scenes work, such as
calls to expand the table.
Bugs to squash.
[ ] Doesn't check for repeated keys in set.
[ ] Doesn't check for matching key in get.
[ ] Doesn't handle collisions.
[ ] The `expand` method is not implemented.
[ ] The `remove` method is not implemented.
Features to add.
[ ] A real implementation of containsKey.
[ ] An iterator for the values.
[ ] An iterator for the keys.
*/

// +-----------+-------------------------------------------------------
// | Constants |
// +-----------+

/**
* The load factor for expanding the table.
*/
static final double LOAD_FACTOR = 0.5;

/**
* The offset to use in linear probes. (We choose a prime because
* that helps ensure that we cover all of the spaces.)
*/
static final double PROBE_OFFSET = 17;

// +--------+----------------------------------------------------------
// | Fields |
// +--------+

/**
* The number of values currently stored in the hash table.
* We use this to determine when to expand the hash table.
*/
int size = 0;

/**
* The array that we use to store the key/value pairs. (We
* use an array, rather than a vector, because we want to
* control expansion.)
*/
Object[] pairs;

/**
* An optional reporter to let us observe what the hash table
* is doing.
*/
Reporter reporter;

/**
* Do we report basic calls?
*/
boolean REPORT_BASIC_CALLS = false;

/**
* Our helpful random number generator, used primarily when
* expanding the size of the table..
*/
Random rand;

// +--------------+----------------------------------------------------
// | Constructors |
// +--------------+

/**
* Create a new hash table.
*/
public HashTable()
{
this.rand = new Random();
this.clear();
this.reporter = null;
} // HashTable

/**
* Create a new hash table that reports activities using a
* reporter.
*/
public HashTable(Reporter reporter)
{
this();
this.reporter = reporter;
} // HashTable(Reporter)

// +-----------+-------------------------------------------------------
// | Observers |
// +-----------+

/**
* Determine if the hash table contains a particular key.
*/
public boolean containsKey(K key)
throws Exception
{
// STUB/HACK
try
{
get(key);
return true;
} // try
catch (Exception e)
{
return false;
} // try/catch
} // containsKey(K)

/**
* Dump the hash table.
*/
public void dump(PrintWriter pen)
{
pen.print("{");
int printed = 0; // Number of elements printed
for (int i = 0; i < this.pairs.length; i++)
{
@SuppressWarnings("unchecked")
KVPair pair = (KVPair) this.pairs[i];
if (pair != null)
{
pen.print(i + ":" + pair.key + "(" + pair.key.hashCode() + "):"
+ pair.value);
if (++printed < this.size)
{
pen.print(", ");
} // if the number printed is less than the size
} // if the current element is not null
} // for
pen.println("}");
} // dump(PrintWriter)

/**
* Get the value for a particular key.
*/
public V get(K key)
throws Exception
{
int index = find(key);
if(!key.equals(this.pairs[index]))
{
throw new Exception("Not the right key!");
}
else {
KVPair pair = (KVPair) pairs[index];
if (pair == null)
{
if (REPORT_BASIC_CALLS && (reporter != null))
{
reporter.report("get(" + key + ") failed");
} // if reporter != null
throw new Exception("Invalid key: " + key);
}
else
{
if (REPORT_BASIC_CALLS && (reporter != null))
{
reporter.report("get(" + key + ") => " + pair.value);
} // if reporter != null
return pair.value;
} // get
}
} // get(K)

/**
* Get the size of the dictionary - the number of values stored.
*/
public int size()
{
return this.size;
} // size()

// +----------+--------------------------------------------------------
// | Mutators |
// +----------+

/**
* Clear the whole dictionary.
*/
public void clear()
{
this.pairs = new Object[41];
this.size = 0;
} // clear()

/**
* Remove a key/value pair.
*/
public void remove(K key)
{
// STUB
} // remove(K)

/**
* Set a value.
*/
public void set(K key, V value)
{
// If there are too many entries, expand the table.
if (this.size > (this.pairs.length * LOAD_FACTOR))
{
expand();
} // if there are too many entries
// Find out where the key belongs and put the pair there.
int index = find(key);
if (this.pairs[index] != null)
{
System.out.println("Already something there");
}
else{
this.pairs[index] = new KVPair(key, value);
// Report activity, if appropriate
if (REPORT_BASIC_CALLS && (reporter != null))
{
reporter.report("pairs[" + index + "] = " + key + ":" + value);
} // if reporter != null
// Note that we've incremented the size.
}
++this.size;
} // set(K,V)

// +------+------------------------------------------------------------
// | Misc |
// +------+

/**
* Should we report basic calls? Intended mostly for tracing.
*/
public void reportBasicCalls(boolean report)
{
REPORT_BASIC_CALLS = report;
} // reportBasicCalls

// +-----------+-------------------------------------------------------
// | Iterators |
// +-----------+

/**
* Get an iterator for the values.
*/
public Iterator<V> iterator()
{
return new Iterator<V>()
{
public V next()
{
// STUB
return null;
} // next()

public boolean hasNext()
{
// STUB
return false;
} // hasNext()

public void remove()
throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
} // remove()
}; // new Iterator<V>
} // iterator()

// +---------+---------------------------------------------------------
// | Helpers |
// +---------+

/**
* Expand the size of the table.
*/
void expand()
{
// Remember the old table.
Object[] old = this.pairs;
// Figure out the size of the new table.
int newSize = 2 * this.pairs.length + rand.nextInt(10);
if (REPORT_BASIC_CALLS && (reporter != null))
{
reporter.report("Expanding to " + newSize + " elements.");
} // if reporter != null
// Create a new table of that size.
// STUB
// Move all the values from the old table to their appropriate
// location in the new table.
// STUB
} // expand()

/**
* Find the index of the entry with a given key. If there is no such
* entry, return the index of an entry we can use to store that key.
*/
int find(K key)
{
int index = Math.abs(key.hashCode()) % this.pairs.length;
while(this.pairs[index] != null)
{
index = (int) (index + this.PROBE_OFFSET) % this.pairs.length;
}
return index;

} // find(K)

// +---------------+---------------------------------------------------
// | Inner Classes |
// +---------------+

/**
* An easy way to hold a key/value pair.
*/
class KVPair
{
K key;
V value;

KVPair(K key, V value)
{
this.key = key;
this.value = value;
} // KVPair(K,V)
} // class KVPair

} // class HashTable<K,V>

Loading

0 comments on commit 3d9a51b

Please sign in to comment.