I have HashMap implementation, but operations are too slowly for me it must be faster, like normal hashmap.
This is the code:
package Map;
public class HashMap<K, V> {
private Entry<K, V>[] table; // Array of Entry.
private int capacity = 4; // Initial capacity of HashMap
static class Entry<K, V> {
K key;
V value;
Entry<K, V> next;
public Entry(K key, V value, Entry<K, V> next) {
this.key = key;
this.value = value;
this.next = next;
}
}
#SuppressWarnings("unchecked")
public HashMap() {
table = new Entry[capacity];
}
/**
* Method allows you put key-value pair in HashMapCustom. If the map already
* contains a mapping for the key, the old value is replaced. Note: method
* does not allows you to put null key though it allows null values.
* Implementation allows you to put custom objects as a key as well. Key
* Features: implementation provides you with following features:- >provide
* complete functionality how to override equals method. >provide complete
* functionality how to override hashCode method.
*
* #param newKey
* #param data
*/
public void put(K newKey, V data) {
if (newKey == null)
return; // does not allow to store null.
// calculate hash of key.
int hash = hash(newKey);
// create new entry.
Entry<K, V> newEntry = new Entry<K, V>(newKey, data, null);
// if table location does not contain any entry, store entry there.
if (table[hash] == null) {
table[hash] = newEntry;
} else {
Entry<K, V> previous = null;
Entry<K, V> current = table[hash];
while (current != null) { // we have reached last entry of bucket.
if (current.key.equals(newKey)) {
if (previous == null) { // node has to be insert on first of
// bucket.
newEntry.next = current.next;
table[hash] = newEntry;
return;
} else {
newEntry.next = current.next;
previous.next = newEntry;
return;
}
}
previous = current;
current = current.next;
}
previous.next = newEntry;
}
}
/**
* Method returns value corresponding to key.
*
* #param key
*/
public V get(K key) {
int hash = hash(key);
if (table[hash] == null) {
return null;
} else {
Entry<K, V> temp = table[hash];
while (temp != null) {
if (temp.key.equals(key))
return temp.value;
temp = temp.next; // return value corresponding to key.
}
return null; // returns null if key is not found.
}
}
public boolean containsKey(K key) {
int hash = hash(key);
if (table[hash] == null) {
return false;
} else {
Entry<K, V> temp = table[hash];
while (temp != null) {
if (temp.key.equals(key))
return true;
temp = temp.next; // return value corresponding to key.
}
}
return false;
}
/**
* Method removes key-value pair from HashMapCustom.
*
* #param key
*/
public boolean remove(K deleteKey) {
int hash = hash(deleteKey);
if (table[hash] == null) {
return false;
} else {
Entry<K, V> previous = null;
Entry<K, V> current = table[hash];
while (current != null) { // we have reached last entry node of
// bucket.
if (current.key.equals(deleteKey)) {
if (previous == null) { // delete first entry node.
table[hash] = table[hash].next;
return true;
} else {
previous.next = current.next;
return true;
}
}
previous = current;
current = current.next;
}
return false;
}
}
/**
* Method displays all key-value pairs present in HashMapCustom., insertion
* order is not guaranteed, for maintaining insertion order refer
* LinkedHashMapCustom.
*
* #param key
*/
public void display() {
for (int i = 0; i < capacity; i++) {
if (table[i] != null) {
Entry<K, V> entry = table[i];
while (entry != null) {
System.out.print("{" + entry.key + "=" + entry.value + "}" + " ");
entry = entry.next;
}
}
}
}
/**
* Method implements hashing functionality, which helps in finding the
* appropriate bucket location to store our data. This is very important
* method, as performance of HashMapCustom is very much dependent on this
* method's implementation.
*
* #param key
*/
private int hash(K key) {
return Math.abs(key.hashCode()) % capacity;
}
}
And when i try put big data, like:
HashMap<Integer,String> map = new HashMap();
long startTime = System.currentTimeMillis();
for(int i = 0 ; i < 200000000; i++){
map.put(i, "kotek"+i);
}
System.out.println(System.currentTimeMillis() - startTime);
It take too long time. I must implement it without another collections like: Set etc. I must put, remove, get and containsKey faster, like normal hashMap, but i don't know how implement that fast map.
The reason why your map is slow is because you have many collisions. Your capacity is 4 and you never expand it. So effectively the put() operation becomes approximately O(N) after the 4 first put() calls. Also as William mentioned, you add your new entries on the end of the bucket. So changing that to adding as the first element will boost the performance . But still it's not a good practice to keep your map in constant size of 4 - since your put() will be fine but get() will still be O(N)
EDIT
You cannot prepend the entry. Since you must go over all the entries on the bucket to make sure you dont already have an equal key
Related
public class SimpleHashMap<k, V> {
public static void main(String args[]){
SimpleHashMap<Integer, Integer> map = new SimpleHashMap<>(10);
map.put(7, 7);
map.put(47897, 47897);
map.put(427, 427);
map.put(77, 77);
map.put(200, 200);
System.out.println(map.get(200));
System.out.println(map.get(7));
System.out.println(map.get(47897));
System.out.println(map.get(427));
System.out.println(map.get(77));
System.out.println(map.get(100));
}
private int size;
private Node<k, V>[] table;
public SimpleHashMap(int capacity) {
table = (Node<k, V>[]) new Node[capacity];
}
public SimpleHashMap() {
this(16);
}
private int index(K key) {
return Math.abs(key.hashCode() % table.length);
}
private Node<K, V> findNode(int index, K key) {
return findNodeRec(index, key, table[index]);
}
private Node<K, V> findNodeRec(int index, K key, Node<K, V> n){
if(n == null){
return null;
}else{
if(n.key.equals(key)){
return n;
}else{
return findNodeRec(index, key, n.next);
}
}
}
/**
* Associates the specified value with the specified key in this map. If the map
* previously contained a mapping for the key, the old value is replaced.
*/
public V put(K key, V value) {
int index = index(key);
Node<K, V> node = findNode(index, key);
if(node == null){
if(table[index] != null){
table[index].next = new Node<>(key, value);
++size;
}else{
table[index] = new Node<>(key, value);
++size;
}
return value;
}else{
table[index].value = value;
return value;
}
}
/**
* Returns the value to which the specified key is mapped, or null if this map
* contains no mapping for the key.
*/
public V get(K key) {
int index = index(key);
Node<K, V> node = findNode(index, key);
if(node != null){
return node.value;
}else{
return null;
}
}
private static class Node<k, V> {
private K key;
private V value;
private Node<k, V> next;
private Node(K key, V value) {
this.key = key;
this.value = value;
this.next = null;
}
}
}
Hashtable
0: 200=200
1:
2:
3:
4:
5:
6:
7: 77=77 427=427 47897=47897 7=7
8:
9:
So I figured out the get method. Problem now is put...
Since Java is call-by-value I can't really recursively call a private put method and pass node.next in since it is the table[index] list I need to modify, either by setting table[index].next to a new one or table[index] or maybe table[index].next.next. The findNode will return any potential nodes that have a certain key. And if a element in table[] has a certain key in it's LinkedList then modify it's value. If it does not have a key create a new node with <K, V>. I can't see how to do this completely. What you right now in the method will create or modify the first and second element (table[index] and table[index].next), again since it's call-by-value I can't really recursively pass a node since any change will just be to that parameter node, not the table[]. So how should I do this?
I am trying to implement a min priority queue with a sorted arraylist. However I am facing trouble for the insertion method. I am getting null pointer exception error. I don't know how to fix it. I have a getKey() and compare method in my AbstractPriorityQueue class. Any help will be really appreciated. I've been stuck with this problem for a long time.
My class:
import java.util.ArrayList;
public class SortedListPQ<K,V> extends AbstractPriorityQueue<K,V> {
private ArrayList<Entry<K,V>> list;
private Comparator<K> comp;
/**
* Constructor
*/
public SortedListPQ() {
list = new ArrayList<Entry<K,V>>();
}
/**
* to check if the queue is empty
*/
public boolean isEmpty() {
return list.size() == 0;
}
/**
* inserting an element in the sorted list (right position)
*/
public Entry<K,V> insert(K key, V value) {
checkKey(key);
Entry<K,V> newest = new PQEntry<>(key, value);
//if the list is empty, then add the entry to the list
if(list.isEmpty()) {
list.add(0,newest);
}
else {
//if the new key is the smallest, add it to the beginning
Entry<K,V>start=list.get(0);
if (comp.compare(start.getKey(),newest.getKey())>0) {
list.add(0,newest);
}
//if the key is largest, we'll add it to the end
Entry<K,V>last=list.get(list.size()-1);
if(comp.compare(last.getKey(), newest.getKey())<0) {
list.add(list.size()-1,newest);
}
//to add the key between two keys
int p=1;
while (p!=list.size()-1) {
Entry<K,V> before = list.get(p);
Entry<K,V> after = list.get(p+1);
if ((comp.compare(before.getKey(),newest.getKey())<0) &&
(comp.compare(after.getKey(),newest.getKey())>0)) {
list.add(p+1,newest);
}
p++;
}
}
return newest;
}
public Entry<K,V> min() {
if (list.size() == 0)
return null;
else
return list.get(0); // the first element is smallest
}
public Entry<K,V> removeMin() {
if (list.size() == 0)
return null;
else // shrink the size
return list.remove(0); // and return the smallest element
}
}
I also have this compare method in my AbstractPriorityQueue class
protected int compare(Entry<K,V> a, Entry<K,V> b) {
return comp.compare(a.getKey( ), b.getKey( ));
}
So I am working on a hashmap that uses two distinct hash functions to create it. The problem is in my iterator class I am getting an error that reads <anonymous hashmap.MyHashMap$1> is not abstract and does not override method in remove() in iterator. I have tried everything to figure out how to get this error to go away. I cannot simply "implement all abstract methods" as it suggests as otherwise I can't initialize the hash function and use it to insert values. Below is the entirety of my code
import java.util.Map;
import java.util.Iterator;
public class MyHashMap<KeyType, ValueType> implements
Iterable<Map.Entry<KeyType, ValueType>> {
// The size of the array
int theSize = 0;
// The starting size of the table
public static final int DEFAULT_ARRAY_SIZE = 11;
// Array containing the number of nodes in each index of the array
int lengths[] = null;
// The array used for the hash table
private Node<KeyType, ValueType>[] arr = null;
private HashFunction<KeyType> hash1; //One of the two hash functions
private HashFunction<KeyType> hash2; //One of the two hash functions
/**
* Null constructor for the myHashMap object
*/
public MyHashMap() {
this(null, null);
}
/**
* Accepts two hash functions to generate a hash code
*
* #param h1 The first hash function
* #param h2 The second hash function
*/
public MyHashMap(HashFunction<KeyType> h1, HashFunction<KeyType> h2) {
hash1 = h1;
hash2 = h2;
doClear();
}
/**
* Returns the size of the table
*/
public int size() {
return theSize;
}
/**
* Performs the clear private routine
*/
public void clear() {
doClear();
}
/**
* Clears the table
*/
private void doClear() {
theSize = 0;
arr = new Node[DEFAULT_ARRAY_SIZE];
lengths = new int[DEFAULT_ARRAY_SIZE];
}
/**
* Resizes the hash table if the size passes the default size
*/
private void rehash() {
MyHashMap<KeyType, ValueType> bigger
= new MyHashMap<KeyType, ValueType>(hash1, hash2);
bigger.arr = new Node[arr.length * 2];
bigger.lengths = new int[bigger.arr.length];
for (Node<KeyType, ValueType> lst : arr) {
for (Node<KeyType, ValueType> p = lst; p != null; p = p.next) {
bigger.put(p.key, p.value);
}
}
arr = bigger.arr;
lengths = bigger.lengths;
bigger = null;
}
/**
* Generates a hash code in order to determine where on the table the item
* goes
*
* #param k The key that will be used to generate the code
* #param h The hash function to be used
* #return The index
*/
private int myHash(KeyType k, HashFunction<KeyType> hash) {
if (hash == null) {
return Math.abs(k.hashCode() % arr.length);
} else {
return Math.abs(hash.hashCode(k) % arr.length);
}
}
/**
* Places an item into the hash map. If the key matches an existing one, the
* value is then replaced with the new incoming value
*
* #param k The incoming key
* #param v The incoming value
* #return The the old value removed.
*/
public ValueType put(KeyType k, ValueType v) {
// If the size of the table exceeds the default array size, rehash
if (size() > arr.length) {
rehash();
}
// The hash codes obtained from the key
int thisList1 = myHash(k, hash1);
int thisList2 = myHash(k, hash2);
// Checking if the incoming key matches an existing key, if it does
// replace the value and return the old one
for (Node<KeyType, ValueType> p = arr[thisList1]; p != null; p = p.next) {
if (p.key.equals(k)) {
ValueType old = p.value;
p.value = v;
return old;
}
}
for (Node<KeyType, ValueType> p = arr[thisList2]; p != null; p = p.next) {
if (p.key.equals(k)) {
ValueType old = p.value;
p.value = v;
return old;
}
}
// Adding the incoming data to the index with the least amount of
// nodes in it.
if (numOfNodes(arr[thisList1]) <= numOfNodes(arr[thisList2])) {
arr[thisList1] = new Node<>(k, v, arr[thisList1]);
++lengths[thisList1];
++theSize;
} else {
// Else the second list is the smaller of the two. Add it to this one
arr[thisList2] = new Node<>(k, v, arr[thisList2]);
++lengths[thisList2];
++theSize;
}
// No old value to return
return null;
}
/**
* Counts the number of nodes that exist in a space of the table
*
* #param n The start space
* #return The number of nodes
*/
private int numOfNodes(Node n) {
int size = 0;
// If the space is not empty, then it will step through and count the
// number of nodes present
for (Node<KeyType, ValueType> p = n; p != null; p = p.next) {
++size;
}
return size;
}
/**
* Removes a specified key from the map
* #param k The key that is to be removed
* #return True if the object has been removed, false if it has not
*/
public boolean remove(KeyType k) {
// Getting the hash codes for the list
int thisList1 = myHash(k, hash1);
int thisList2 = myHash(k, hash2);
// If the position specified by hash1 is found, check first element
if (arr[thisList1] != null) {
if (arr[thisList1].key.equals(k)) {
arr[thisList1] = arr[thisList1].next;
--theSize;
return true;
}
for (Node<KeyType, ValueType> p = arr[thisList1]; p.next != null;
p = p.next) {
if (p.next.key.equals(k)) {
p.next = p.next.next;
--theSize;
return true;
}
}
}
// If the position specified by hash2 is found, check first element
if (arr[thisList2] != null) {
if (arr[thisList2].key.equals(k)) {
arr[thisList2] = arr[thisList2].next;
--theSize;
return true;
}
for (Node<KeyType, ValueType> p = arr[thisList2]; p.next != null;
p = p.next) {
if (p.next.key.equals(k)) {
p.next = p.next.next;
--theSize;
return true;
}
}
}
// Else the item that is to removed is never found, therefore return
// false
return false;
}
/**
* Returns the desired value associated with the specified key
*
* #param k The key
* #return The value if found, null if not
*/
public ValueType get(KeyType k) {
// The hash codes obtained from the key
int thisList1 = myHash(k, hash1);
int thisList2 = myHash(k, hash2);
// Using the first hash code, searching the nodes
for (Node<KeyType, ValueType> p = arr[thisList1]; p != null; p = p.next) {
if (p.key.equals(k)) {
return p.value;
}
}
// Using the second hash code, seraching the nodes
for (Node<KeyType, ValueType> p = arr[thisList2]; p != null; p = p.next) {
if (p.key.equals(k)) {
return p.value;
}
}
return null;
}
/**
* Prints out the hashMap
*
* #return The printed hashMap
*/
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
for(Map.Entry<KeyType, ValueType> n : this) {
sb.append(n);
sb.append(",");
}
sb.append("]");
return new String(sb);
}
/**
* Creates an iterator for traversing the list.
*
* #return An iterator to traverse the list
*/
#Override
public Iterator<Map.Entry<KeyType, ValueType>> iterator() {
return new Iterator<Map.Entry<KeyType, ValueType>>() {
/**
* Checks if the node has a next link
* #return True if it has a node, false if it does not
*/
#Override
public boolean hasNext() {
return current != null;
}
/**
* Gets the next item on the list
* #return The next node
*/
#Override
public Map.Entry<KeyType, ValueType> next() {
final Node<KeyType, ValueType> theCurrent = current;
current = current.next;
// If the current list has no more nodes, move to the next list
if (current == null) {
++listNum;
advanceToNewList();
}
// Creates a new entry that will hold the next node
Map.Entry<KeyType, ValueType> nextItem = new Map.Entry<KeyType, ValueType>() {
/**
* Returns the current node's key
* #return
*/
#Override
public KeyType getKey() {
return theCurrent.key;
}
/**
* Returns the current node's value
* #return
*/
#Override
public ValueType getValue() {
return theCurrent.value;
}
/**
* Sets a new value to the current node
* #param value The new value that will be placed
* #return The old value that was overwritten
*/
#Override
public ValueType setValue(ValueType value) {
ValueType temp = theCurrent.value;
theCurrent.value = value;
return temp;
}
};
return nextItem;
}
/**
* Advances to the next index of the table
*/
private void advanceToNewList() {
while (listNum < arr.length && arr[ listNum ] == null) {
listNum++;
}
if (listNum != arr.length) {
current = arr[listNum];
}
}
// Initializes the advanceToNewList method {
advanceToNewList();
}
Node<KeyType, ValueType> current; // current node
int listNum; // current list #
};
}
/**
* Creates a node class for the linked list
* #param <KeyType> The key for the map. Can be any type
* #param <ValueType> The value associated with the key. Can be any type
*/
class Node<KeyType, ValueType> {
/**
* Constructs a node object using a key, a value, and a link to the next
* node
* #param k The key
* #param v The value
* #param n The next node
*/
Node(KeyType k, ValueType v, Node<KeyType, ValueType> n) {
key = k;
value = v;
next = n;
}
/**
* Prints out the node
* #return A string containing the key and the value
*/
public String toString() {
return key + "=" + value;
}
KeyType key; // The key of the node
ValueType value; // The value of the node
Node<KeyType, ValueType> next; // The next node
}
/**
* Creates an array that contains the number of entries that have a
* certain length. For example: The number of lists of length 0, 1, 2, etc.
* #return The array containing the distribution of list lengths
*/
public int[] getLengths()
{
int sizeList[] = new int[20];
// Incrementing the count at the index in the array sizeList. It will
// increment for every list of size at lengths[i].
for(int i = 0; i < lengths.length; ++i) {
++sizeList[lengths[i]];
}
return sizeList;
}
}
Below is the specific location where the error occurs
public Iterator<Map.Entry<KeyType, ValueType>> iterator() {
return new Iterator<Map.Entry<KeyType, ValueType>>()
{ <--- Right here is where the error occurs
Any help that can be provided will be greatly appreciated
Short answer: When creating anonymous classes of an interface, you must implement every abstract method. You don't necessarily need to thoroughly implement every method, you must just create it even if it has blank contents. So, you can just do this to rid the error (starting at the arrow where the exception was raised) :
public void remove() {
//No contents
}
and do the same things for any other abstract methods in the Iterator interface. However, it is probably best that you do actually write some code for these...
I built a symbol table using an array list filled with an object "pairs" that are singly-linked chains and hold a word and the number of times it occurs in a text file. I need to use this for the FrequencyCounter program that counts the number of words in a file. For some reason, I'm getting this error when running the the FrequencyCounter with the HashST:
Processed 1215985 words (19 sec; 19710 msec
Exception in thread "main" java.lang.NullPointerException
at HashST.hashIndex(HashST.java:60)
at HashST.get(HashST.java:105)
at FrequencyCounter.main(FrequencyCounter.java:112)
I have an idea that there's something wrong with my HashST and its not putting the pairs in the ArrayList like I wanted it to. Any suggestions on what is wrong with the implementation would be greatly appreciated.
Here is my code and the code for the FrequencyCounter:
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.Iterator;
public class HashST<Key extends Comparable<Key>, Value> implements Iterable<Key> {
private ArrayList<Pair> chains;
private int numKeys;
private int numChains;
public class Pair
{
Key key;
Value value;
Pair(Key k, Value v)
{
key = k;
value = v;
}
Pair()
{}
Pair next;
}
/**
* Initialize an empty HashSt with a default of 64 empty chains.
*/
public HashST()
{
this(64);
}
/**
* Initialize an empty HashST with numChains emptychains.
* 387911 is a prime number about twice the number of distinct
* words in the leipzig1M.txt file.
*/
public HashST(int numChains)
{
this.numChains = numChains;
chains = new ArrayList<Pair>();
for(int i = 0; i < numChains; i++)
{
Pair p = new Pair(null, null);
chains.add(p);
}
}
/**
* compute the hash index for a key k if the number of
* chains is N
*/
private int hashIndex(Key k, int N)
{
return (k.hashCode() & 0x7fffffff) % N;
}
/**
* insert the Pair (k,v) into the appropriate chain and increment
* the number of keys counter or
* update the value for k if k is already in the hash table.
*
*/
public void put(Key k, Value v) {
int i = hashIndex(k, numChains);
Pair tmp = chains.get(i);
if(contains(k))
{
while(tmp.next != null)
{
if(tmp.key == k)
{
tmp.value = v;
return;
}
tmp = tmp.next;
}
}
else
{
Pair p = new Pair(k, v);
tmp.next = p;
numKeys ++;
}
}
/**
* return the value for key k if it is in the hash table
* or else return null if it is not.
*/
public Value get(Key k) {
int i = hashIndex(k, numChains);
Pair tmp = chains.get(i);
while(tmp.next != null)
{
if(tmp.key == k)
{
return tmp.value;
}
tmp = tmp.next;
}
return null;
}
/**
* remove the pair with key k if it is in the hash table
* otherwise no change.
*/
public void delete(Key k) {
if(contains(k))
{
return;
}
}
/**
* return true if the hash table contains a pair with key
* equal to k else return false
*/
public boolean contains(Key k) {
return (get(k) != null) ? true : false;
}
/**
* return the number of keys in the hash table
*/
public int size() {
return numKeys;
}
/**
* return a LinkedList<Key> containing the keys in the
* hash table
*/
public Iterable<Key> keys() {
LinkedList<Key> l = new LinkedList<Key>();
for(Pair p : chains)
{
while(p.next != null)
{
l.add(p.key);
p = p.next;
}
}
return l;
}
/**
* return an Iterator<Key> for the keys in the hash table
*/
public Iterator<Key> iterator() {
return keys().iterator();
}
}
And here is the Frequency Counter: http://algs4.cs.princeton.edu/31elementary/FrequencyCounter.java.html
According to your stack trace this would seem to be the line that threw a null pointer:
return (k.hashCode() & 0x7fffffff) % N;
So we have one object reference k, an integer constant, and a primitive N. Neither the constant nor the primitive can be null, the only thing being dereferenced here is k. So it looks like someone tried to get a value for a null k!
extendible hashing is one of the best hashing method,I want to create program in java, for extenidble hashing. is there any api available for doing that? i dont get the clear algorithm for doing that myself, so if there is no api, .if possible post algoirhtm
I'm just curious, why do you need to implement such an algorithm? Are the standard Java Map implementations not performing for you? If you are suffering from an issue of buckets becoming too heavily loaded you may want to take a look at the hashCode() method before opting for non-standard routes. An alternative could also be to look at some of the options provided by GNU Trove.
Finally - a similar algorithm to Extendible is Cuckoo hashing. Some information below:
http://en.wikipedia.org/wiki/Cuckoo_hashing
Source code here:
http://lmonson.com/blog/?page_id=99
This file defines a HashTable class. Keys and values in the hash table
are of type Object. Keys cannot be null. The default constructor
creates a table that initially has 64 locations, but a different
initial size can be specified as a parameter to the constructor.
The table increases in size if it becomes more than 3/4 full.
public class HashTable {
static private class ListNode {
// Keys that have the same hash code are placed together
// in a linked list. This private nested class is used
// internally to implement linked lists. A ListNode
// holds a (key,value) pair.
Object key;
Object value;
ListNode next; // Pointer to next node in the list;
// A null marks the end of the list.
}
private ListNode[] table; // The hash table, represented as
// an array of linked lists.
private int count; // The number of (key,value) pairs in the
// hash table.
public HashTable() {
// Create a hash table with an initial size of 64.
table = new ListNode[64];
}
public HashTable(int initialSize) {
// Create a hash table with a specified initial size.
// Precondition: initalSize > 0.
table = new ListNode[initialSize];
}
void dump() {
// This method is NOT part of the usual interface for
// a hash table. It is here only to be used for testing
// purposes, and should be removed before the class is
// released for general use. This lists the (key,value)
// pairs in each location of the table.
System.out.println();
for (int i = 0; i < table.length; i++) {
// Print out the location number and the list of
// key/value pairs in this location.
System.out.print(i + ":");
ListNode list = table[i]; // For traversing linked list number i.
while (list != null) {
System.out.print(" (" + list.key + "," + list.value + ")");
list = list.next;
}
System.out.println();
}
} // end dump()
public void put(Object key, Object value) {
// Associate the specified value with the specified key.
// Precondition: The key is not null.
int bucket = hash(key); // Which location should this key be in?
ListNode list = table[bucket]; // For traversing the linked list
// at the appropriate location.
while (list != null) {
// Search the nodes in the list, to see if the key already exists.
if (list.key.equals(key))
break;
list = list.next;
}
// At this point, either list is null, or list.key.equals(key).
if (list != null) {
// Since list is not null, we have found the key.
// Just change the associated value.
list.value = value;
}
else {
// Since list == null, the key is not already in the list.
// Add a new node at the head of the list to contain the
// new key and its associated value.
if (count >= 0.75*table.length) {
// The table is becoming too full. Increase its size
// before adding the new node.
resize();
}
ListNode newNode = new ListNode();
newNode.key = key;
newNode.value = value;
newNode.next = table[bucket];
table[bucket] = newNode;
count++; // Count the newly added key.
}
}
public Object get(Object key) {
// Retrieve the value associated with the specified key
// in the table, if there is any. If not, the value
// null will be returned.
int bucket = hash(key); // At what location should the key be?
ListNode list = table[bucket]; // For traversing the list.
while (list != null) {
// Check if the specified key is in the node that
// list points to. If so, return the associated value.
if (list.key.equals(key))
return list.value;
list = list.next; // Move on to next node in the list.
}
// If we get to this point, then we have looked at every
// node in the list without finding the key. Return
// the value null to indicate that the key is not in the table.
return null;
}
public void remove(Object key) {
// Remove the key and its associated value from the table,
// if the key occurs in the table. If it does not occur,
// then nothing is done.
int bucket = hash(key); // At what location should the key be?
if (table[bucket] == null) {
// There are no keys in that location, so key does not
// occur in the table. There is nothing to do, so return.
return;
}
if (table[bucket].key.equals(key)) {
// If the key is the first node on the list, then
// table[bucket] must be changed to eliminate the
// first node from the list.
table[bucket] = table[bucket].next;
count--; // Remove new number of items in the table.
return;
}
// We have to remove a node from somewhere in the middle
// of the list, or at the end. Use a pointer to traverse
// the list, looking for a node that contains the
// specified key, and remove it if it is found.
ListNode prev = table[bucket]; // The node that precedes
// curr in the list. Prev.next
// is always equal to curr.
ListNode curr = prev.next; // For traversing the list,
// starting from the second node.
while (curr != null && ! curr.key.equals(key)) {
curr = curr.next;
prev = curr;
}
// If we get to this point, then either curr is null,
// or curr.key is equal to key. In the later case,
// we have to remove curr from the list. Do this
// by making prev.next point to the node after curr,
// instead of to curr. If curr is null, it means that
// the key was not found in the table, so there is nothing
// to do.
if (curr != null) {
prev.next = curr.next;
count--; // Record new number of items in the table.
}
}
public boolean containsKey(Object key) {
// Test whether the specified key has an associated value
// in the table.
int bucket = hash(key); // In what location should key be?
ListNode list = table[bucket]; // For traversing the list.
while (list != null) {
// If we find the key in this node, return true.
if (list.key.equals(key))
return true;
list = list.next;
}
// If we get to this point, we know that the key does
// not exist in the table.
return false;
}
public int size() {
// Return the number of key/value pairs in the table.
return count;
}
private int hash(Object key) {
// Compute a hash code for the key; key cannot be null.
// The hash code depends on the size of the table as
// well as on the value returned by key.hashCode().
return (Math.abs(key.hashCode())) % table.length;
}
private void resize() {
// Double the size of the table, and redistribute the
// key/value pairs to their proper locations in the
// new table.
ListNode[] newtable = new ListNode[table.length*2];
for (int i = 0; i < table.length; i++) {
// Move all the nodes in linked list number i
// into the new table. No new ListNodes are
// created. The existing ListNode for each
// key/value pair is moved to the newtable.
// This is done by changing the "next" pointer
// in the node and by making a pointer in the
// new table point to the node.
ListNode list = table[i]; // For traversing linked list number i.
while (list != null) {
// Move the node pointed to by list to the new table.
ListNode next = list.next; // The is the next node in the list.
// Remember it, before changing
// the value of list!
int hash = (Math.abs(list.key.hashCode())) % newtable.length;
// hash is the hash code of list.key that is
// appropriate for the new table size. The
// next two lines add the node pointed to by list
// onto the head of the linked list in the new table
// at position number hash.
list.next = newtable[hash];
newtable[hash] = list;
list = next; // Move on to the next node in the OLD table.
}
}
table = newtable; // Replace the table with the new table.
} // end resize()
} // end class HashTable
A Program for Testing HashTable:
A little program to test the HashTable class. Note that I start with a really small table so that I can easily test the resize() method.
public class TestHashTable {
public static void main(String[] args){
HashTable table = new HashTable(2);
String key,value;
while (true) {
System.out.println("\nMenu:");
System.out.println(" 1. test put(key,value)");
System.out.println(" 2. test get(key)");
System.out.println(" 3. test containsKey(key)");
System.out.println(" 4. test remove(key)");
System.out.println(" 5. show complete contents of hash table.");
System.out.println(" 6. EXIT");
System.out.print("Enter your command: ");
switch ( TextIO.getlnInt()) {
case 1:
System.out.print("\n Key = ");
key = TextIO.getln();
System.out.print(" Value = ");
value = TextIO.getln();
table.put(key,value);
break;
case 2:
System.out.print("\n Key = ");
key = TextIO.getln();
System.out.println(" Value is " + table.get(key));
break;
case 3:
System.out.print("\n Key = ");
key = TextIO.getln();
System.out.println(" containsKey(" + key + ") is "
+ table.containsKey(key));
break;
case 4:
System.out.print("\n Key = ");
key = TextIO.getln();
table.remove(key);
break;
case 5:
table.dump();
break;
case 6:
return; // End program by returning from main()
default:
System.out.println(" Illegal command.");
break;
}
System.out.println("\nHash table size is " + table.size());
}
}
} // end class TestHashTable
Preface: I do not know what is extendible hashing.
Based on what I understood from this wiki(http://en.wikipedia.org/wiki/Extendible_hashing), that it can search using maximum two lookups, you might want to look at the Java implementation of Bernstein DB (sg-cDB).
http://cr.yp.to/cdb.html
A Java implementation of extendible hashing:
http://en.wikibooks.org/wiki/Data_Structures/Hash_Tables#A_Java_implementation_of_extendible_hashing
As srini.venigalla says... the sg-cDB of Bernstein is good. You can found a ported version of CDB in pure Java made by Michael Alyn Miller in GitHub: https://github.com/malyn/sg-cdb
// Java program to demonstrate implementation of our
// own hash table with chaining for collision detection
import java.util.ArrayList;
// A node of chains
class HashNode<K, V>
{
K key;
V value;
// Reference to next node
HashNode<K, V> next;
// Constructor
public HashNode(K key, V value)
{
this.key = key;
this.value = value;
}
}
// Class to represent entire hash table
class Map<K, V>
{
// bucketArray is used to store array of chains
private ArrayList<HashNode<K, V>> bucketArray;
// Current capacity of array list
private int numBuckets;
// Current size of array list
private int size;
// Constructor (Initializes capacity, size and
// empty chains.
public Map()
{
bucketArray = new ArrayList<>();
numBuckets = 10;
size = 0;
// Create empty chains
for (int i = 0; i < numBuckets; i++)
bucketArray.add(null);
}
public int size() { return size; }
public boolean isEmpty() { return size() == 0; }
// This implements hash function to find index
// for a key
private int getBucketIndex(K key)
{
int hashCode = key.hashCode();
int index = hashCode % numBuckets;
return index;
}
// Method to remove a given key
public V remove(K key)
{
// Apply hash function to find index for given key
int bucketIndex = getBucketIndex(key);
// Get head of chain
HashNode<K, V> head = bucketArray.get(bucketIndex);
// Search for key in its chain
HashNode<K, V> prev = null;
while (head != null)
{
// If Key found
if (head.key.equals(key))
break;
// Else keep moving in chain
prev = head;
head = head.next;
}
// If key was not there
if (head == null)
return null;
// Reduce size
size--;
// Remove key
if (prev != null)
prev.next = head.next;
else
bucketArray.set(bucketIndex, head.next);
return head.value;
}
// Returns value for a key
public V get(K key)
{
// Find head of chain for given key
int bucketIndex = getBucketIndex(key);
HashNode<K, V> head = bucketArray.get(bucketIndex);
// Search key in chain
while (head != null)
{
if (head.key.equals(key))
return head.value;
head = head.next;
}
// If key not found
return null;
}
// Adds a key value pair to hash
public void add(K key, V value)
{
// Find head of chain for given key
int bucketIndex = getBucketIndex(key);
HashNode<K, V> head = bucketArray.get(bucketIndex);
// Check if key is already present
while (head != null)
{
if (head.key.equals(key))
{
head.value = value;
return;
}
head = head.next;
}
// Insert key in chain
size++;
head = bucketArray.get(bucketIndex);
HashNode<K, V> newNode = new HashNode<K, V>(key, value);
newNode.next = head;
bucketArray.set(bucketIndex, newNode);
// If load factor goes beyond threshold, then
// double hash table size
if ((1.0*size)/numBuckets >= 0.7)
{
ArrayList<HashNode<K, V>> temp = bucketArray;
bucketArray = new ArrayList<>();
numBuckets = 2 * numBuckets;
size = 0;
for (int i = 0; i < numBuckets; i++)
bucketArray.add(null);
for (HashNode<K, V> headNode : temp)
{
while (headNode != null)
{
add(headNode.key, headNode.value);
headNode = headNode.next;
}
}
}
}
// Driver method to test Map class
public static void main(String[] args)
{
Map<String, Integer>map = new Map<>();
map.add("this",1 );
map.add("coder",2 );
map.add("this",4 );
map.add("hi",5 );
System.out.println(map.size());
System.out.println(map.remove("this"));
System.out.println(map.remove("this"));
System.out.println(map.size());
System.out.println(map.isEmpty());
}
}
private ITEM[] st; private int N, M; ST(int maxN) { N = 0; M = 4; st = new ITEM[M]; } private void expand() { ITEM[] t = st; N = 0; M = M+M; st = new ITEM[M]; for (int i = 0; i < M/2; i++) if (t[i] != null) insert(t[i]); } void insert(ITEM x) { int i = hash(x.key(), M); while (st[i] != null) i = (i+1) % M; st[i] = x; if (N++ >= M/2) expand(); }
/*
A little program to test the HashTable class. Note that I
start with a really small table so that I can easily test
the resize() method.
*/
public class TestHashTable {
public static void main(String[] args){
HashTable table = new HashTable(2);
String key,value;
while (true) {
System.out.println("\nMenu:");
System.out.println(" 1. test put(key,value)");
System.out.println(" 2. test get(key)");
System.out.println(" 3. test containsKey(key)");
System.out.println(" 4. test remove(key)");
System.out.println(" 5. show complete contents of hash table.");
System.out.println(" 6. EXIT");
System.out.print("Enter your command: ");
switch ( TextIO.getlnInt()) {
case 1:
System.out.print("\n Key = ");
key = TextIO.getln();
System.out.print(" Value = ");
value = TextIO.getln();
table.put(key,value);
break;
case 2:
System.out.print("\n Key = ");
key = TextIO.getln();
System.out.println(" Value is " + table.get(key));
break;
case 3:
System.out.print("\n Key = ");
key = TextIO.getln();
System.out.println(" containsKey(" + key + ") is "
+ table.containsKey(key));
break;
case 4:
System.out.print("\n Key = ");
key = TextIO.getln();
table.remove(key);
break;
case 5:
table.dump();
break;
case 6:
return; // End program by returning from main()
default:
System.out.println(" Illegal command.");
break;
}
System.out.println("\nHash table size is " + table.size());
}
}
} // end class TestHashTable
Hashtable implements a key value pair kind of collection
*/
import java.util.*;
public class HashtableDemo {
static String newLine = System.getProperty("line.separator");
public static void main(String[] args) {
System.out.println(newLine + "Hashtable in Java" + newLine);
System.out.println("-----------------------" + newLine);
System.out.println("Adding items to the Hashtable" + newLine);
//Creating Hashtable object
//dictionary can be created using HashTable object
//as dictionary is an abstract class
Hashtable ht = new Hashtable();
//you add elements to Hashtable using put method
//put(key, value)
ht.put("Java", 1);
ht.put(".NET", 2);
ht.put("Javascript", 3 );
ht.put("HTML", 4);
//You will see that the items will be arranged as per the hash function
System.out.println(newLine + "Items in the Hashtable..." + ht + newLine);
System.out.println("-----------------------" + newLine);
//looping through all the elements in hashtable
String str;
//you can retrieve all the keys in hashtable using .keys() method
Enumeration names = ht.keys();
while(names.hasMoreElements()) {
//next element retrieves the next element in the dictionary
str = (String) names.nextElement();
//.get(key) returns the value of the key stored in the hashtable
System.out.println(str + ": " + ht.get(str) + newLine);
}
}
}