Please ask questions when you encounter some language issues that are unclear....
To provide a common interface, Goodrich first define an Entry interface:
public interface Entry<K,V> { /** Returns the key stored in this entry. */ public K getKey(); /** Returns the value stored in this entry. */ public V getValue(); } |
public class HashEntry<K,V> implements Entry<K,V> { protected K key; protected V value; public HashEntry(K k, V v) { key = k; value = v; } public V getValue() { return value; } public K getKey() { return key; } public V setValue(V val) { V oldValue = value; value = val; return oldValue; } public boolean equals(Object o) { HashEntry |
|
public class A // Outer class { public static class B // Inner (nested) class { public void methodB1() { .... } } .... } |
|
public class MyProg { public static void main(String[] args) { A.B x = new A.B(); // defining variable x.methodB1(); // Invoking method ... } } |
public class A // Outer class { public class B // Inner class { public void methodB1() { .... } } .... } |
|
public class MyProg { public static void main(String[] args) { A x = new A(); // defining variable x.x.methodB1(); // Invoking method ... } } |
public class HashTableMap<K,V> implements Map<K,V> { /** Static nested class for an entry in a hash table. */ public static class HashEntry<K,V> implements Entry<K,V> { .... (see above for the definition) } (Variables and methods of the Map implementation) } |
public interface Map<K,V> { /** Returns the number of items in the map. */ public int size(); /** Returns whether the map is empty. */ public boolean isEmpty(); /** Puts a given key-value pair in the map, replacing a previous * one, if any, and returns the old value. */ public V put(K key, V value) throws InvalidKeyException; /** Returns the value associated with a key. */ public V get(K key) throws InvalidKeyException; /** Removes the key-value pair with a given key. */ public V remove(K key) throws InvalidKeyException; /** Returns an iterable object containing all the keys in the map. */ public Iterable<K> keySet(); /** Returns an iterable object containing all the values in the map. */ public Iterable<V> values(); /** Returns an iterable object containing all the entries in the map. */ public Iterable<Entry<K,V>> entrySet(); } |
|
public class HashTableMap<K,V> implements Map<K,V> { protected Entry<K,V> AVAILABLE = new HashEntry<K,V>(null, null); // Dummy entry to occupy the DELETED slots protected int n = 0; // Actual number of entries in the Map protected int prime; // Prime number used in the hash function protected int capacity; // = N (capacity of bucket array) public Entry<K,V>[] bucket; // bucket array // I made it public to print :-) protected long scale; // Scaling factor (a) of the MAD compression protected long shift; // Shift factor (b) of the MAD compression .... } |
public class HashTableMap<K,V> implements Map<K,V> { ... public HashTableMap() // Default constructor... { this(109345121,1000); // Calls the actual constructor below // 109345121 is the default prime number :-) } public HashTableMap(int cap) { this(109345121, cap); // Calls the actual constructor below } public HashTableMap(int p, int cap) { prime = p; capacity = cap; bucket = (Entry<K,V>[]) new HashEntry[capacity]; // Create an array of HashEntries // It's a safe cast /* --------------------------------------------------- Select a and b for MAD compression: Hash index = ( (a × hashCode + b) % p ) % N ---------------------------------------------------- */ java.util.Random rand = new java.util.Random(); scale = rand.nextInt(prime-1) + 1; // integer from range [1..(p-1)] shift = rand.nextInt(prime); // integer from range [0..(p-1)] } ... } |
/** --------------------------------------------------------------- Helper search method: - if the key is found, it returns index >= 0 - if the key is not found, it returns -a - 1 (a neg value), where a is the index of the first empty/available slot ------------------------------------------------------------------ */ protected int findEntry(K key) throws InvalidKeyException { int avail = -1; // Flag indicating an initial search int i; int j; checkKey(key); // Make sure "key" is correct type of object i = hashValue(key); // Hash index j = i; // Stop criteria... stop when i == j // See: click here do { Entry<K,V> e; e = bucket[i]; // (Initial) Hash location if ( e == null ) { /* Reach an EMPTY slot */ if ( avail < 0 ) { avail = i; // Remember the first EMPTY slot } break; } if ( key.equals(e.getKey()) ) // we have found our key { return i; // key found, return index of array that stores the key } if ( e == AVAILABLE ) { // This slot is open (was "previousle delete") if ( avail < 0 ) avail = i; // First time -> remember that FIRST available (EMPTY) slot } i = (i + 1) % capacity; // Linear probe, look at the next location } while (i != j); /* -------------------------------------------- At this point: 1. k is NOT found 1. avail = index of the FIRST empty slot -------------------------------------------- */ return -avail - 1; // Return indication of not found and the index // of the first empty/available slot // Note: the actual index is "avail", and avail canbe 0 // We subtract 1 to make sure that the return value is < 0 // To get "avail" back: -value - 1 } |
|
|
/** Returns the value associated with a key. */ public V get (K key) throws InvalidKeyException { int i; i = findEntry(key); // Call helper method to finding a key if ( i < 0 ) return null; // there is no value for this key, so return null return bucket[i].getValue(); // return the found value in this case } |
|
public V put (K key, V value) throws InvalidKeyException { int i; HashEntry<K,V> oldValue; i = findEntry(key); //find the appropriate spot for this entry if ( i >= 0 ) // this key has an existing value { /* Replace existing value */ oldValue = (HashEntry<K,V>) bucket[i]).setValue(value); return (oldValue); // Return old value } /* --------------------------------------- Insert (new) entry in empty position --------------------------------------- */ index j; j = (-i) - 1; // convert i to an array index bucket[(-i)-1)] = new HashEntry<K,V>(key, value); // Store the entry n++; // One more entry in Map return null; // there was no previous value, return null... } |
|
/** -------------------------------------------------- Removes the key-value pair with a specified key. -------------------------------------------------- */ public V remove (K key) throws InvalidKeyException { int i; i = findEntry(key); // find this key first // i >= 0, then i = index of item with key k // i < 0, then (-i)-1 = index of first empty slot if ( i < 0 ) { // Item to delete is not in the map.... return null; // nothing to remove } V toReturn; toReturn = bucket[i].getValue(); // Save the old value to return bucket[i] = AVAILABLE; // mark this slot as deactivated n--; return toReturn; } |
|
|
|
protected void rehash() { Entry<K,V> e; int i, j, k; capacity = 2*capacity; // Double the array size Entry<K,V>[] old = bucket; // The old Map bucket = (Entry<K,V>[]) new Entry[capacity]; // the new map java.util.Random rand = new java.util.Random(); scale = rand.nextInt(prime-1) + 1; // new hash scaling factor shift = rand.nextInt(prime); // new hash shifting factor /* ---------------------------------------------- Re-hash every entry (loop through the array) ----------------------------------------------- */ for ( i = 0; i < old.length; i++) { e = old[i]; // Process new array element if ( (e != null) && (e != AVAILABLE) ) { // e is a valid entry (don't hash AVAILABLE :-)) k = findEntry(e.getKey()); // k = index of e.key in new array j = (-k) - 1; // k must be negative because key is new bucket[j] = e; // Put entry e at position j of new Map } } } |
public V put (K key, V value) throws InvalidKeyException { int i; HashEntry<K,V> oldValue; i = findEntry(key); //find the appropriate spot for this entry if ( i >= 0 ) // this key has an existing value { /* Replace existing value */ oldValue = (HashEntry<K,V>) bucket[i]).setValue(value); return (oldValue); // Return old value } /* ------------------------------------------------------- ******* Check for the need to RE-HASH ------------------------------------------------------- */ if ( n >= capacity/2 ) { rehash(); // rehash to keep the load factor <= 0.5 i = findEntry(key); // find again the appropriate spot for this entry } /* --------------------------------------------------------- **END** Check for the need to RE-HASH --------------------------------------------------------- */ /* --------------------------------------- Insert (new) entry in empty position --------------------------------------- */ bucket[(-i)-1)] = new HashEntry<K,V>(key, value); // (-i)+1 convert i to a proper index n++; // One more entry in Map return null; // there was no previous value, return null... } |
Here is the directory where I have Goodrich code compiled (and ready to run):
|
|
WE hold these Truths to be self-evident, that all Men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the Pursuit of Happiness -- That to secure these Rights, Governments are instituted among Men, deriving their just Powers from the Consent of the Governed, that whenever any Form of Government becomes destructive of these Ends, it is the Right of the People to alter or to abolish it, and to institute new Government, laying its Foundation on such Principles, and organizing its Powers in such Form, as to them shall seem most likely to effect their Safety and Happiness. Prudence, indeed, will dictate that Governments long established should not be changed for light and transient Causes; and accordingly all Experience hath shewn, that Mankind are more disposed to suffer, while Evils are sufferable, than to right themselves by abolishing the Forms to which they are accustomed. But when a long Train of Abuses and Usurpations, pursuing invariably the same Object, evinces a Design to reduce them under absolute Despotism, it is their Right, it is their Duty, to throw off such Government, and to provide new Guards for their future Security. Such has been the patient Sufferance of these Colonies; and such is now the Necessity which constrains them to alter their former Systems of Government. The History of the present King of Great-Britain is a History of repeated Injuries and Usurpations, all having in direct Object the Establishment of an absolute Tyranny over these States. To prove this, let Facts be submitted to a candid World. |
Output:
|
public class WordCount { public static void main(String[] args) throws IOException { Scanner cin = new Scanner(System.in); cin.useDelimiter("[^a-zA-Z]"); // ignore non-letters HashTableMap<String,Integer> h = new HashTableMap<String,Integer>(); String word; Integer count; while ( cin.hasNext() ) { word = cin.next(); if (word.equals("")) continue; // ignore null strings between delimiters word = word.toLowerCase(); // ignore case count = h.get(word); // get the previous count for this word /* ************************************** Update count of word in Map ************************************** */ if ( count == null ) h.put(word, 1); // First occurence else h.put(word, ++count); // Increase count and update Map entry } /* ================================ Find most frequent word in input ================================ */ int maxCount = 0; String maxWord = "no word"; for ( Entry<String,Integer> ent : h.entrySet() ) { // find max-count word if ( ent.getValue() > maxCount ) { maxWord = ent.getKey(); // More frequent word found maxCount = ent.getValue(); } } System.out.print("The most frequent word is \"" + maxWord); System.out.println(",\" with word-count = " + maxCount + "."); } } |
Program location:
|