import java.util.ArrayList; import java.util.Map; /** * A simple implementation of a hash table using linear probing to resolve collisions. * * Particular operations this implementation does *not* support include iteration, containsValue, * and automatic resizing/rehashing (fixed capacity only). */ public class SimpleHashMap extends AbstractSimpleMap implements Map { /** * A single key/value pair in the hash table. */ private static class Entry { private K key; private V value; public Entry(K key, V value) { this.key = key; this.value = value; } } // raw data of the hash table (not all slots may be occupied) private Entry[] data; // number of entries in the hash table private int count; // placeholder object for a deleted slot // needed for linear probing across deleted slots private Entry reserved; // create a new hash table with the given number of bins public SimpleHashMap(int capacity) { data = new Entry[capacity]; count = 0; // note: the reserved object has null key and value, but is itself *not null* reserved = new Entry(null, null); } @Override public int size() { return count; } @Override public boolean isEmpty() { return count > 0; } // grab the slot for a particular key - returns the slot where the given // key is located, or the unoccupied slot where the given key should go private int locate(Object key) { // compute an initial hash code int hash = Math.abs(key.hashCode() % data.length); // keep track of first unused slot, in case we need it int reservedSlot = -1; boolean foundReserved = false; // note: this loop assumes we don't run out of capacity (might get infinite loop // if array is full) while (data[hash] != null) { // there's something here - either reserved or not if (data[hash] == reserved) { // remember reserved slot if we fail to locate value if (!foundReserved) { reservedSlot = hash; foundReserved = true; } } else { // a 'real' value, check it if (key.equals(data[hash].key)) { // value located - return the index in table return hash; } } // linear probing - just walk forward and wrap around if at end hash = (1 + hash) % data.length; } // return first empty slot we encountered if (foundReserved) { return reservedSlot; } else { return hash; } } @Override public boolean containsKey(Object key) { Entry entry = data[locate(key)]; return entry != null && entry != reserved; } @Override public V get(Object key) { Entry entry = data[locate(key)]; if (entry == null || entry == reserved) { return null; } else { return entry.value; } } @Override public V put(K key, V value) { int hash = locate(key); Entry entry = data[hash]; if (entry == null || entry == reserved) { // logically empty data[hash] = new Entry(key, value); count++; return null; // return existing mapping for this key (none) } else { // occupied slot; overwrite and return old Entry existing = data[hash]; V oldValue = existing.value; existing.value = value; return oldValue; } } @Override public V remove(Object key) { int hash = locate(key); Entry entry = data[hash]; if (entry == null || entry == reserved) { return null; } count--; V oldValue = entry.value; data[hash] = reserved; // set to placeholder return oldValue; } // run a test of the SimpleHashMap class public static void main(String[] args) { Map cityPops = new SimpleHashMap(100); cityPops.put("Brunswick", 20645); cityPops.put("Portland", 66937); cityPops.put("Falmouth", 12102); System.out.println("Population of Brunswick is " + cityPops.get("Brunswick")); System.out.println("Population of Falmouth is " + cityPops.get("Falmouth")); System.out.println("Population of Portland is " + cityPops.get("Portland")); System.out.println("Population of nowhere is " + cityPops.get("nowhere")); cityPops.remove("Falmouth"); System.out.println("Population after removal is " + cityPops.get("Falmouth")); System.out.println("Population of Brunswick is " + cityPops.get("Brunswick")); } }