So I have a HashTable implementation here that I wrote using only Arrays and had a little bit of help with the code. Unfortunately, I don't quite understand one of the lines someone added while running the "get" or "put" method. What exactly is happening in the while loop below? It is a method for linear probing correct? Also why is the loop checking the conditions it's checking?
Specifically,
int hash = hashThis(key);
while(data[hash] != AVAILABLE && data[hash].key() != key) {
hash = (hash + 1) % capacity;
}
Here's the whole Java class below for full reference.
public class Hashtable2 {
private Node[] data;
private int capacity;
private static final Node AVAILABLE = new Node("Available", null);
public Hashtable2(int capacity) {
this.capacity = capacity;
data = new Node[capacity];
for(int i = 0; i < data.length; i++) {
data[i] = AVAILABLE;
}
}
public int hashThis(String key) {
return key.hashCode() % capacity;
}
public Object get(String key) {
int hash = hashThis(key);
while(data[hash] != AVAILABLE && data[hash].key() != key) {
hash = (hash + 1) % capacity;
}
return data[hash].element();
}
public void put(String key, Object element) {
if(key != null) {
int hash = hashThis(key);
while(data[hash] != AVAILABLE && data[hash].key() != key) {
hash = (hash + 1) % capacity;
}
data[hash] = new Node(key, element);
}
}
public String toString(){
String s="<";
for (int i=0;i<this.capacity;i++)
{
s+=data[i]+", ";
}
s+=">";
return s;
}
Thank you.
I just rewrote some part of the code and added the findHash-method - try to avoid code-duplication!
private int findHash(String key) {
int hash = hashThis(key);
// search for the next available element or for the next matching key
while(data[hash] != AVAILABLE && data[hash].key() != key) {
hash = (hash + 1) % capacity;
}
return hash;
}
public Object get(String key) {
return data[findHash(key)].element();
}
public void put(String key, Object element) {
data[findHash(key)] = new Node(key, element);
}
What you asked for is - what exactly does this findHash-loop? The data was initialized with AVAILABLE - meaning: the data does not (yet) contain any actual data. Now - when we add an element with put - first a hashValue is calculated, that is just an index in the data array where to put the data. Now - if we encounter that the position has already been taken by another element with the same hash value but a different key, we try to find the next AVAILABLE position. And the get method essentially works the same - if a data element with a different key is detected, the next element is probed and so on.
The data itself is a so called ring-buffer. That is, it is searched until the end of the array and is next search again at the beginning, starting with index 0. This is done with the modulo % operator.
Alright?
Sample Hashtable implementation using Generics and Linear Probing for collision resolution. There are some assumptions made during implementation and they are documented in javadoc above class and methods.
This implementation doesn't have all the methods of Hashtable like keySet, putAll etc but covers most frequently used methods like get, put, remove, size etc.
There is repetition of code in get, put and remove to find the index and it can be improved to have a new method to find index.
class HashEntry<K, V> {
private K key;
private V value;
public HashEntry(K key, V value) {
this.key = key;
this.value = value;
}
public void setKey(K key) { this.key = key; }
public K getKey() { return this.key; }
public void setValue(V value) { this.value = value; }
public V getValue() { return this.value; }
}
/**
* Hashtable implementation ...
* - with linear probing
* - without loadfactor & without rehash implementation.
* - throws exception when table is full
* - returns null when trying to remove non existent key
*
* #param <K>
* #param <V>
*/
public class Hashtable<K, V> {
private final static int DEFAULT_CAPACITY = 16;
private int count;
private int capacity;
private HashEntry<K, V>[] table;
public Hashtable() {
this(DEFAULT_CAPACITY);
}
public Hashtable(int capacity) {
super();
this.capacity = capacity;
table = new HashEntry[capacity];
}
public boolean isEmpty() { return (count == 0); }
public int size() { return count; }
public void clear() { table = new HashEntry[this.capacity]; count = 0; }
/**
* Returns null if either probe count is higher than capacity else couldn't find the element.
*
* #param key
* #return
*/
public V get(K key) {
V value = null;
int probeCount = 0;
int hash = this.hashCode(key);
while (table[hash] != null && !table[hash].getKey().equals(key) && probeCount <= this.capacity) {
hash = (hash + 1) % this.capacity;
probeCount++;
}
if (table[hash] != null && probeCount <= this.capacity) {
value = table[hash].getValue();
}
return value;
}
/**
* Check on the no of probes done and terminate if probe count reaches to its capacity.
*
* Throw Exception if table is full.
*
* #param key
* #param value
* #return
* #throws Exception
*/
public V put(K key, V value) throws Exception {
int probeCount = 0;
int hash = this.hashCode(key);
while (table[hash] != null && !table[hash].getKey().equals(key) && probeCount <= this.capacity) {
hash = (hash + 1) % this.capacity;
probeCount++;
}
if (probeCount <= this.capacity) {
if (table[hash] != null) {
table[hash].setValue(value);
} else {
table[hash] = new HashEntry(key, value);
count++;
}
return table[hash].getValue();
} else {
throw new Exception("Table Full!!");
}
}
/**
* If key present then mark table[hash] = null & return value, else return null.
*
* #param key
* #return
*/
public V remove(K key) {
V value = null;
int probeCount = 0;
int hash = this.hashCode(key);
while (table[hash] != null && !table[hash].getKey().equals(key) && probeCount <= this.capacity) {
hash = (hash + 1) % this.capacity;
probeCount++;
}
if (table[hash] != null && probeCount <= this.capacity) {
value = table[hash].getValue();
table[hash] = null;
count--;
}
return value;
}
public boolean contains(Object value) {
return this.containsValue(value);
}
public boolean containsKey(Object key) {
for (HashEntry<K, V> entry : table) {
if (entry != null && entry.getKey().equals(key)) {
return true;
}
}
return false;
}
public boolean containsValue(Object value) {
for (HashEntry<K, V> entry : table) {
if (entry != null && entry.getValue().equals(value)) {
return true;
}
}
return false;
}
#Override
public String toString() {
StringBuilder data = new StringBuilder();
data.append("{");
for (HashEntry<K, V> entry : table) {
if (entry != null) {
data.append(entry.getKey()).append("=").append(entry.getValue()).append(", ");
}
}
if (data.toString().endsWith(", ")) {
data.delete(data.length() - 2, data.length());
}
data.append("}");
return data.toString();
}
private int hashCode(K key) { return (key.hashCode() % this.capacity); }
public static void main(String[] args) throws Exception {
Hashtable<Integer, String> table = new Hashtable<Integer, String>(2);
table.put(1, "1");
table.put(2, "2");
System.out.println(table);
table.put(1, "3");
table.put(2, "4");
System.out.println(table);
table.remove(1);
System.out.println(table);
table.put(1, "1");
System.out.println(table);
System.out.println(table.get(1));
System.out.println(table.get(3));
// table is full so below line
// will throw an exception
table.put(3, "2");
}
}
Sample run of the above code.
{2=2, 1=1}
{2=4, 1=3}
{2=4}
{2=4, 1=1}
1
null
Exception in thread "main" java.lang.Exception: Table Full!!
at Hashtable.put(Hashtable.java:95)
at Hashtable.main(Hashtable.java:177)
Related
I am writing my own hashmap. I am able to define put/get methods. When I am trying to resize the array, I am getting null values:
public void resize() {
if (100 * size >= 75 * capacity) {
int oldcap = capacity;
capacity = capacity * 2;
Entry[] resizedBuckets = new Entry[capacity];
for (int i = 0; i < oldcap; i++) {
resizedBuckets[i] = this.buckets[i];
}
this.buckets = resizedBuckets;
}
}
My whole code is as below:
package lib;
class Entry<K, V> {
K key;
V value;
Entry<K, V> next;
Entry(K key, V value, Entry next) {
this.key = key;
this.value = value;
}
}
public class MyMap<K, V> {
Entry[] buckets;
int size = 0;
static int capacity = 2;
MyMap(int capacity) {
this.buckets = new Entry[capacity];
}
public MyMap() {
this(capacity);
}
public void resize() {
if (100 * size >= 75 * capacity) {
int oldcap = capacity;
capacity = capacity * 2;
Entry[] resizedBuckets = new Entry[capacity];
for (int i = 0; i < oldcap; i++) {
resizedBuckets[i] = this.buckets[i];
}
this.buckets = resizedBuckets;
}
}
public void put(K key, V value) {
int bucket = key.hashCode() % capacity;
//i have bucket it
Entry newEntry = new Entry<K, V>(key, value, null);
if (this.buckets[bucket] == null) {
this.buckets[bucket] = newEntry;
size++;
} else {
Entry currentNode = this.buckets[bucket];
Entry prevNode = null;
while (currentNode.next != null && currentNode.key != key) {
prevNode = currentNode;
currentNode = currentNode.next;
}
if (currentNode.key == key) {
if (prevNode == null) {
newEntry.next = currentNode.next;
this.buckets[bucket] = newEntry;
} else {
newEntry.next = currentNode.next;
prevNode.next = newEntry;
}
} else {
currentNode.next = newEntry;
size++;
}
}
resize();
}
public V get(K key) {
int bucket = key.hashCode() % capacity;
Entry currentBucket = this.buckets[bucket];
while (currentBucket != null) {
if (currentBucket.key == key)
return (V) currentBucket.value;
currentBucket = currentBucket.next;
}
return null;
}
}
class MyMain {
public static void main(String[] args) {
MyMap<String, Integer> myMap = new MyMap<>();
myMap.put("name", 1);
myMap.put("2name", 2);
myMap.put("3name", 2);
myMap.put("4name", 3);
myMap.put("5name", 2);
myMap.put("6name", 2);
myMap.put("3name", 3);
System.out.println(myMap);
System.out.println(myMap.get("name"));
System.out.println(myMap.get("2name"));
System.out.println(myMap.get("3name"));
}
}
I am getting following output:
lib.MyMap#30f39991
null
null
3
I should be getting:
1
2
3
What's the reason? I think the issue is with resize method, but I am unable to figure it out.
As mentioned in the comments your bucket index changed as capacity was increased during the resize operation. And thus int bucket = key.hashCode() % capacity; inside put and get returns a different bucket index than before.
I haven't tested it, but I think, if you do
resizedBuckets[this.buckets[i].getKey().hashCode() % capacity] = this.buckets[i];
inside the resize method, this might already work.
As #Voo pointed out, the above change only works as long as you don't have any hash collisions before resizing. For a complete solution you will need to recalculate the index for every key!
Have a look at your code:
public void put(K key, V value) {
int bucket = key.hashCode() % capacity;
...
resize(); // this will modify capacity;
}
public void get(K key) {
int bucket = key.hashCode() % capacity;
...
}
You resize() the map, but store the old hashCode() x capacity key bucket. This invalidates the key somewhat for your get()-method. Because if you would put the same value-key-pair again with your modified map size, it would not land in the same bucket as the old key-value-pair.
You have to store the keys in your resized map with the new capacity instead of simply copying them.
In advance, I apologize for my lack of experience, these are advanced concepts that are difficult to wrap my head around. From what I understand, linear probing is circular, it won't stop until it finds an empty cell.
However I am not sure how to implement it. Some example on how to would be greatly appreciated. Sorry again for the inexperience, I'm not some vetted programmer, I'm picking this up very slowly.
public boolean ContainsElement(V element)
{
for(int i = 0; i < capacity; i++)
{
if(table[i] != null)
{
LinkedList<Entry<K, V>> bucketMethod = table[i];
for(Entry<K, V> entry : bucketMethod)
{
if(entry.getElement().equals(element))
{
return true;
}
}
}
}
return false;
}
Here's a working hash table based on the pseudocode examples found in the Wikipedia article for open addressing.
I think the main differences between the Wikipedia example and mine are:
Treating the hashCode() a little bit due to the way Java does modulo (%) with negative numbers.
Implemented simple resizing logic.
Changed the logic in the remove method a little bit because Java doesn't have goto.
Otherwise, it's more or less just a direct translation.
package mcve;
import java.util.*;
import java.util.stream.*;
public class OAHashTable {
private Entry[] table = new Entry[16]; // Must be >= 4. See findSlot.
private int size = 0;
public int size() {
return size;
}
private int hash(Object key) {
int hashCode = Objects.hashCode(key)
& 0x7F_FF_FF_FF; // <- This is like abs, but it works
// for Integer.MIN_VALUE. We do this
// so that hash(key) % table.length
// is never negative.
return hashCode;
}
private int findSlot(Object key) {
int i = hash(key) % table.length;
// Search until we either find the key, or find an empty slot.
//
// Note: this becomes an infinite loop if the key is not already
// in the table AND every element in the array is occupied.
// With the resizing logic (below), this will only happen
// if the table is smaller than length=4.
while ((table[i] != null) && !Objects.equals(table[i].key, key)) {
i = (i + 1) % table.length;
}
return i;
}
public Object get(Object key) {
int i = findSlot(key);
if (table[i] != null) { // Key is in table.
return table[i].value;
} else { // Key is not in table
return null;
}
}
private boolean tableIsThreeQuartersFull() {
return ((double) size / (double) table.length) >= 0.75;
}
private void resizeTableToTwiceAsLarge() {
Entry[] old = table;
table = new Entry[2 * old.length];
size = 0;
for (Entry e : old) {
if (e != null) {
put(e.key, e.value);
}
}
}
public void put(Object key, Object value) {
int i = findSlot(key);
if (table[i] != null) { // We found our key.
table[i].value = value;
return;
}
if (tableIsThreeQuartersFull()) {
resizeTableToTwiceAsLarge();
i = findSlot(key);
}
table[i] = new Entry(key, value);
++size;
}
public void remove(Object key) {
int i = findSlot(key);
if (table[i] == null) {
return; // Key is not in the table.
}
int j = i;
table[i] = null;
--size;
while (true) {
j = (j + 1) % table.length;
if (table[j] == null) {
break;
}
int k = hash(table[j].key) % table.length;
// Determine if k lies cyclically in (i,j]
// | i.k.j |
// |....j i.k.| or |.k..j i...|
if ( (i<=j) ? ((i<k)&&(k<=j)) : ((i<k)||(k<=j)) ) {
continue;
}
table[i] = table[j];
i = j;
table[i] = null;
}
}
public Stream<Entry> entries() {
return Arrays.stream(table).filter(Objects::nonNull);
}
#Override
public String toString() {
return entries().map(e -> e.key + "=" + e.value)
.collect(Collectors.joining(", ", "{", "}"));
}
public static class Entry {
private Object key;
private Object value;
private Entry(Object key, Object value) {
this.key = key;
this.value = value;
}
public Object getKey() { return key; }
public Object getValue() { return value; }
}
public static void main(String[] args) {
OAHashTable t = new OAHashTable();
t.put("A", 1);
t.put("B", 2);
t.put("C", 3);
System.out.println("size = " + t.size());
System.out.println(t);
t.put("X", 4);
t.put("Y", 5);
t.put("Z", 6);
t.remove("C");
t.remove("B");
t.remove("A");
t.entries().map(e -> e.key)
.map(key -> key + ": " + t.get(key))
.forEach(System.out::println);
}
}
java.util.HashMap implementation of java.util.Map internally provides linear probing that is HashMap can resolve collisions in hash tables.
When I'm trying to print to print out data I'm getting this error saying: Cannot find symbol.
Below is my method
public int heightOfBinaryTree(Node node) {
if (node == null) {
return 0;
} else {
return 1
+ Math.max(heightOfBinaryTree(node.left),
heightOfBinaryTree(node.right));
}
}
This is my print in the main
System.out.println(Math.max(heightOfBinaryTree(node.left)));
Full code
public class Binaireboom<Key extends Comparable<Key>, Value> {
private Node root; // root of BST
private class Node {
private Key key; // sorted by key
private Value val; // associated data
private Node left, right; // left and right subtrees
private int N; // number of nodes in subtree
private int aantal;
int height;
public Node(Key key, Value val, int N, int aantal) {
this.key = key;
this.val = val;
this.N = N;
this.aantal = aantal;
}
}
/**
* Initializes an empty symbol tables.
*/
public Binaireboom() {
}
/**
* Returns true if this symbol tables is empty.
*
* #return <tt>true</tt> if this symbol tables is empty; <tt>false</tt>
* otherwise
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* Returns the number of key-value pairs in this symbol tables.
*
* #return the number of key-value pairs in this symbol tables.
*/
public int size() {
return size(root);
}
// return number of key-value pairs in BST rooted at x
private int size(Node x) {
if (x == null) {
return 0;
} else {
return x.N;
}
}
/**
* Does this symbol tables contain the given key?
*
* #param key the key
* #return <tt>true</tt> if this symbol tables contains <tt>key</tt> and
* <tt>false</tt> otherwise
* #throws NullPointerException if <tt>key</tt> is <tt>null</tt>
*/
public boolean contains(Key key) {
if (key == null) {
throw new NullPointerException("argument to contains() is null");
}
return get(key) != null;
}
/**
* Returns the value associated with the given key.
*
* #param key the key
* #return the value associated with the given key if the key is in the
* symbol tables and <tt>null</tt> if the key is not in the symbol tables
* #throws NullPointerException if <tt>key</tt> is <tt>null</tt>
*/
public Value get(Key key) {
return get(root, key);
}
private Value get(Node x, Key key) {
if (x == null) {
return null;
}
int cmp = key.compareTo(x.key);
if (cmp < 0) {
return get(x.left, key);
} else if (cmp > 0) {
return get(x.right, key);
} else {
return x.val;
}
}
public int getAantal(Key key) {
return getAantal(root, key);
}
private int getAantal(Node x, Key key) {
if (x == null) {
return 0;
}
int cmp = key.compareTo(x.key);
if (cmp < 0) {
return getAantal(x.left, key);
} else if (cmp > 0) {
return getAantal(x.right, key);
} else {
return x.aantal;
}
}
/**
* Inserts the specified key-value pair into the symbol tables, overwriting
* the old value with the new value if the symbol tables already contains
* the specified key. Deletes the specified key (and its associated value)
* from this symbol tables if the specified value is <tt>null</tt>.
*
* #param key the key
* #param val the value
* #throws NullPointerException if <tt>key</tt> is <tt>null</tt>
*/
public void put(Key key, Value val) {
if (key == null) {
throw new NullPointerException("first argument to put() is null");
}
if (val == null) {
delete(key);
return;
}
root = put(root, key, val);
assert check();
}
private Node put(Node x, Key key, Value val) {
if (x == null) {
return new Node(key, val, 1, 1);
}
int cmp = key.compareTo(x.key);
if (cmp < 0) {
x.left = put(x.left, key, val);
} else if (cmp > 0) {
x.right = put(x.right, key, val);
} else if (cmp == 0) {
x.aantal++;
} else {
x.val = val;
}
x.N = 1 + size(x.left) + size(x.right);
return x;
}
/**
* Removes the smallest key and associated value from the symbol tables.
*
* #throws NoSuchElementException if the symbol tables is empty
*/
public void deleteMin() {
if (isEmpty()) {
throw new NoSuchElementException("Symbol tables underflow");
}
root = deleteMin(root);
assert check();
}
private Node deleteMin(Node x) {
if (x.left == null) {
return x.right;
}
x.left = deleteMin(x.left);
x.N = size(x.left) + size(x.right) + 1;
return x;
}
/**
* Removes the largest key and associated value from the symbol tables.
*
* #throws NoSuchElementException if the symbol tables is empty
*/
public void deleteMax() {
if (isEmpty()) {
throw new NoSuchElementException("Symbol tables underflow");
}
root = deleteMax(root);
assert check();
}
private Node deleteMax(Node x) {
if (x.right == null) {
return x.left;
}
x.right = deleteMax(x.right);
x.N = size(x.left) + size(x.right) + 1;
return x;
}
/**
* Removes the specified key and its associated value from this symbol
* tables (if the key is in this symbol tables).
*
* #param key the key
* #throws NullPointerException if <tt>key</tt> is <tt>null</tt>
*/
public void delete(Key key) {
if (key == null) {
throw new NullPointerException("argument to delete() is null");
}
root = delete(root, key);
assert check();
}
private Node delete(Node x, Key key) {
if (x == null) {
return null;
}
int cmp = key.compareTo(x.key);
if (cmp < 0) {
x.left = delete(x.left, key);
} else if (cmp > 0) {
x.right = delete(x.right, key);
} else {
if (x.right == null) {
return x.left;
}
if (x.left == null) {
return x.right;
}
Node t = x;
x = min(t.right);
x.right = deleteMin(t.right);
x.left = t.left;
}
x.N = size(x.left) + size(x.right) + 1;
return x;
}
/**
* Returns the smallest key in the symbol tables.
*
* #return the smallest key in the symbol tables
* #throws NoSuchElementException if the symbol tables is empty
*/
public Key min() {
if (isEmpty()) {
throw new NoSuchElementException("called min() with empty symbol tables");
}
return min(root).key;
}
private Node min(Node x) {
if (x.left == null) {
return x;
} else {
return min(x.left);
}
}
/**
* Returns the largest key in the symbol tables.
*
* #return the largest key in the symbol tables
* #throws NoSuchElementException if the symbol tables is empty
*/
public Key max() {
if (isEmpty()) {
throw new NoSuchElementException("called max() with empty symbol tables");
}
return max(root).key;
}
private Node max(Node x) {
if (x.right == null) {
return x;
} else {
return max(x.right);
}
}
/**
* Returns the largest key in the symbol tables less than or equal to
* <tt>key</tt>.
*
* #param key the keys
* #return the largest key in the symbol tables less than or equal to
* <tt>key</tt>
* #throws NoSuchElementException if there is no such key
* #throws NullPointerException if <tt>key</tt> is <tt>null</tt>
*/
public Key floor(Key key) {
if (key == null) {
throw new NullPointerException("argument to floor() is null");
}
if (isEmpty()) {
throw new NoSuchElementException("called floor() with empty symbol tables");
}
Node x = floor(root, key);
if (x == null) {
return null;
} else {
return x.key;
}
}
private Node floor(Node x, Key key) {
if (x == null) {
return null;
}
int cmp = key.compareTo(x.key);
if (cmp == 0) {
return x;
}
if (cmp < 0) {
return floor(x.left, key);
}
Node t = floor(x.right, key);
if (t != null) {
return t;
} else {
return x;
}
}
/**
* Returns the smallest key in the symbol tables greater than or equal to
* <tt>key</tt>.
*
* #param key the keys
* #return the smallest key in the symbol tables greater than or equal to
* <tt>key</tt>
* #throws NoSuchElementException if there is no such key
* #throws NullPointerException if <tt>key</tt> is <tt>null</tt>
*/
public Key ceiling(Key key) {
if (key == null) {
throw new NullPointerException("argument to ceiling() is null");
}
if (isEmpty()) {
throw new NoSuchElementException("called ceiling() with empty symbol tables");
}
Node x = ceiling(root, key);
if (x == null) {
return null;
} else {
return x.key;
}
}
private Node ceiling(Node x, Key key) {
if (x == null) {
return null;
}
int cmp = key.compareTo(x.key);
if (cmp == 0) {
return x;
}
if (cmp < 0) {
Node t = ceiling(x.left, key);
if (t != null) {
return t;
} else {
return x;
}
}
return ceiling(x.right, key);
}
/**
* Return the kth smallest key in the symbol tables.
*
* #param k the order statistic
* #return the kth smallest key in the symbol tables
* #throws IllegalArgumentException unless <tt>k</tt> is between 0 and
* <em>N</em> − 1
*/
public Key select(int k) {
if (k < 0 || k >= size()) {
throw new IllegalArgumentException();
}
Node x = select(root, k);
return x.key;
}
// Return key of rank k.
private Node select(Node x, int k) {
if (x == null) {
return null;
}
int t = size(x.left);
if (t > k) {
return select(x.left, k);
} else if (t < k) {
return select(x.right, k - t - 1);
} else {
return x;
}
}
/**
* Return the number of keys in the symbol tables strictly less than
* <tt>key</tt>.
*
* #param key the key
* #return the number of keys in the symbol tables strictly less than
* <tt>key</tt>
* #throws NullPointerException if <tt>key</tt> is <tt>null</tt>
*/
public int rank(Key key) {
if (key == null) {
throw new NullPointerException("argument to rank() is null");
}
return rank(key, root);
}
// Number of keys in the subtree less than key.
private int rank(Key key, Node x) {
if (x == null) {
return 0;
}
int cmp = key.compareTo(x.key);
if (cmp < 0) {
return rank(key, x.left);
} else if (cmp > 0) {
return 1 + size(x.left) + rank(key, x.right);
} else {
return size(x.left);
}
}
/**
* Returns all keys in the symbol tables as an <tt>Iterable</tt>. To iterate
* over all of the keys in the symbol tables named <tt>st</tt>, use the
* foreach notation: <tt>for (Key key : st.keys())</tt>.
*
* #return all keys in the symbol tables
*/
public Iterable<Key> keys() {
return keys(min(), max());
}
/**
* Returns all keys in the symbol tables in the given range, as an
* <tt>Iterable</tt>.
*
* #return all keys in the sybol tables between <tt>lo</tt>
* (inclusive) and <tt>hi</tt> (exclusive)
* #throws NullPointerException if either <tt>lo</tt> or <tt>hi</tt>
* is <tt>null</tt>
*/
public Iterable<Key> keys(Key lo, Key hi) {
if (lo == null) {
throw new NullPointerException("first argument to keys() is null");
}
if (hi == null) {
throw new NullPointerException("second argument to keys() is null");
}
Queue<Key> queue = new Queue<Key>();
keys(root, queue, lo, hi);
return queue;
}
private void keys(Node x, Queue<Key> queue, Key lo, Key hi) {
if (x == null) {
return;
}
int cmplo = lo.compareTo(x.key);
int cmphi = hi.compareTo(x.key);
if (cmplo < 0) {
keys(x.left, queue, lo, hi);
}
if (cmplo <= 0 && cmphi >= 0) {
queue.enqueue(x.key);
}
if (cmphi > 0) {
keys(x.right, queue, lo, hi);
}
}
/**
* Returns the number of keys in the symbol tables in the given range.
*
* #return the number of keys in the sybol tables between <tt>lo</tt>
* (inclusive) and <tt>hi</tt> (exclusive)
* #throws NullPointerException if either <tt>lo</tt> or <tt>hi</tt>
* is <tt>null</tt>
*/
public int size(Key lo, Key hi) {
if (lo == null) {
throw new NullPointerException("first argument to size() is null");
}
if (hi == null) {
throw new NullPointerException("second argument to size() is null");
}
if (lo.compareTo(hi) > 0) {
return 0;
}
if (contains(hi)) {
return rank(hi) - rank(lo) + 1;
} else {
return rank(hi) - rank(lo);
}
}
/**
* Returns the height of the BST (for debugging).
*
* #return the height of the BST (a 1-node tree has height 0)
*/
public int height() {
return height(root);
}
private int height(Node x) {
if (x == null) {
return -1;
}
return 1 + Math.max(height(x.left), height(x.right));
}
/**
* Returns the keys in the BST in level orders (for debugging).
*
* #return the keys in the BST in level orders traversal
*/
public Iterable<Key> levelOrder() {
Queue<Key> keys = new Queue<Key>();
Queue<Node> queue = new Queue<Node>();
queue.enqueue(root);
while (!queue.isEmpty()) {
Node x = queue.dequeue();
if (x == null) {
continue;
}
keys.enqueue(x.key);
queue.enqueue(x.left);
queue.enqueue(x.right);
}
return keys;
}
/**
* ***********************
* Check integrity of BST data structures.
************************
*/
private boolean check() {
if (!isBST()) {
StdOut.println("Not in symmetric order");
}
if (!isSizeConsistent()) {
StdOut.println("Subtree counts not consistent");
}
if (!isRankConsistent()) {
StdOut.println("Ranks not consistent");
}
return isBST() && isSizeConsistent() && isRankConsistent();
}
// does this binary tree satisfy symmetric order?
// Note: this test also ensures that data structure is a binary tree since order is strict
private boolean isBST() {
return isBST(root, null, null);
}
// is the trees rooted at x a BST with all keys strictly between min and max
// (if min or max is null, treat as empty constraint)
// Credit: Bob Dondero's elegant solution
private boolean isBST(Node x, Key min, Key max) {
if (x == null) {
return true;
}
if (min != null && x.key.compareTo(min) <= 0) {
return false;
}
if (max != null && x.key.compareTo(max) >= 0) {
return false;
}
return isBST(x.left, min, x.key) && isBST(x.right, x.key, max);
}
// are the size fields correct?
private boolean isSizeConsistent() {
return isSizeConsistent(root);
}
private boolean isSizeConsistent(Node x) {
if (x == null) {
return true;
}
if (x.N != size(x.left) + size(x.right) + 1) {
return false;
}
return isSizeConsistent(x.left) && isSizeConsistent(x.right);
}
// check that ranks are consistent
private boolean isRankConsistent() {
for (int i = 0; i < size(); i++) {
if (i != rank(select(i))) {
return false;
}
}
for (Key key : keys()) {
if (key.compareTo(select(rank(key))) != 0) {
return false;
}
}
return true;
}
public int heightOfBinaryTree(Node node) {
if (node == null) {
return 0;
} else {
return 1
+ Math.max(heightOfBinaryTree(node.left),
heightOfBinaryTree(node.right));
}
}
/**
* Unit tests the <tt>BST</tt> data type.
*/
public static void main(String[] args) throws FileNotFoundException, IOException {
long startTime = System.currentTimeMillis();
//heightOfBinaryTree hbt = new heightOfBinaryTree();
Binaireboom<String, Integer> st = new Binaireboom<String, Integer>();
BufferedReader file = null;
int i = 0;
file = new BufferedReader(new FileReader(new File("")));
String TemporaryVar;
while ((TemporaryVar = file.readLine()) != null) {
TemporaryVar = TemporaryVar.replaceAll("[`~!##$%^&*()_|+\\-=?;:'\",.<>\\{\\}\\[\\]\\\\\\/]", "");
String[] words = TemporaryVar.split(" ");
for (String word : words) {
if (word == null) {
continue;
}
st.put(word, 1);
i++;
}
}
StdOut.println();
for (String s : st.keys()) {
StdOut.println(s + " " + st.getAantal(s));
}
System.out.println(i);
System.out.println();
System.out.println("elapsed: " + (System.currentTimeMillis() - startTime));
System.out.println(Math.max(heightOfBinaryTree(node.left)));
}
}
No where in your main method have you declared a value for node...
You need a declaration
Node node = new Node(args....)
or
Node node = someobject.getNode(arg..);
It wasnt immediately obvious to me how you construct a Node from this class.
ie where that would come from...
I am preparing my own custom HashMap implementation in Java. Below is my imlementation.
public class Entry<K,V> {
private final K key;
private V value;
private Entry<K,V> next;
public Entry(K key, V value, Entry<K,V> next) {
this.key = key;
this.value = value;
this.next = next;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
public Entry<K, V> getNext() {
return next;
}
public void setNext(Entry<K, V> next) {
this.next = next;
}
public K getKey() {
return key;
}
}
public class MyCustomHashMap<K,V> {
private int DEFAULT_BUCKET_COUNT = 10;
private Entry<K,V>[] buckets;
public MyCustomHashMap() {
buckets = new Entry[DEFAULT_BUCKET_COUNT];
for (int i = 0;i<DEFAULT_BUCKET_COUNT;i++)
buckets[i] = null;
}
public void put(K key,V value){
/**
* This is the new node.
*/
Entry<K,V> newEntry = new Entry<K,V>(key, value, null);
/**
* If key is null, then null keys always map to hash 0, thus index 0
*/
if(key == null){
buckets[0] = newEntry;
}
/**
* get the hashCode of the key.
*/
int hash = hash(key);
/**
* if the index does of the bucket does not contain any element then assign the node to the index.
*/
if(buckets[hash] == null) {
buckets[hash] = newEntry;
} else {
/**
* we need to traverse the list and compare the key with each of the keys till the keys match OR if the keys does not match then we need
* to add the node at the end of the linked list.
*/
Entry<K,V> previous = null;
Entry<K,V> current = buckets[hash];
while(current != null) {
boolean done = false;
while(!done) {
if(current.getKey().equals(key)) {
current.setValue(value);
done = true; // if the keys are same then replace the old value with the new value;
} else if (current.getNext() == null) {
current.setNext(newEntry);
done = true;
}
current = current.getNext();
previous = current;
}
}
previous.setNext(newEntry);
}
}
public V getKey(K key) {
int hash = hash(key);
if(buckets[hash] == null) {
return null;
} else {
Entry<K,V> temp = buckets[hash];
while(temp != null) {
if(temp.getKey().equals(key))
return temp.getValue(); // returns value corresponding to key.
temp = temp.getNext();
}
return null; //return null if key is not found.
}
}
public void display() {
for(int i = 0; i < DEFAULT_BUCKET_COUNT; i++) {
if(buckets[i] != null) {
Entry<K,V> entry = buckets[i];
while(entry != null){
System.out.print("{"+entry.getKey()+"="+entry.getValue()+"}" +" ");
entry=entry.getNext();
}
}
}
}
public int bucketIndexForKey(K key) {
int bucketIndex = key.hashCode() % buckets.length;
return bucketIndex;
}
/**
*
* #param key
* #return
*/
private int hash(K key){
return Math.abs(key.hashCode()) % buckets.length;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
MyCustomHashMap<String, Integer> myCustomHashMap = new MyCustomHashMap<String, Integer>();
myCustomHashMap.put("S", 22);
myCustomHashMap.put("S", 1979);
myCustomHashMap.put("V", 5);
myCustomHashMap.put("R", 31);
System.out.println("Value corresponding to key R: "+myCustomHashMap.getKey("R"));
System.out.println("Value corresponding to key V: "+myCustomHashMap.getKey("V"));
System.out.println("Displaying the contents of the HashMap:: ");
myCustomHashMap.display();
}
}
1) I feel that put (K key,V value) is somewhat flawed. Please do kindly validate and let me know what's wrong here. On entering the same key its giving me wrong result. I have not yet tested it for collision cases having different keys.
2) It is said that we rehash the hashCode so that it eliminates wrong implementation of hashCode. how do I do it because if I give hashCode of key i.e. hash(key.hashCode()) then it dosn't take as it can't compute hashCode of int. How to do this?
Any help would be highly appreciated.
Thanks
Sid
You handle null key incorrectly :
if(key == null){
buckets[0] = newEntry;
}
It's possible that buckets[0] already contains entries, in which case you will lose those entries.
The following loop has some issues :
Entry<K,V> previous = null;
Entry<K,V> current = buckets[hash];
while(current != null) {
boolean done = false;
while(!done) {
if(current.getKey().equals(key)) {
current.setValue(value);
done = true;
} else if (current.getNext() == null) {
current.setNext(newEntry);
done = true;
}
current = current.getNext();
previous = current; // you are not really setting previous to
// to the previous Entry in the list - you
// are setting it to the current Entry
}
}
previous.setNext(newEntry); // you don't need this statement. You
// already have a statement inside the
// loop that adds the new Entry to the list
It looks like removing any statements related to previous will fix this loop.
EDIT:
As kolakao commented, in order for your implementation to be efficient (i.e. require expected constant time for get and put), you must resize the HashMap when the number of entries exceeds some threshold (in order for the average number of entries in each bucket to be bound by a constant).
It is said that we rehash the hashCode so that it eliminates wrong implementation of hashCode. how do I do it because if I give hashCode of key i.e. hash(key.hashCode()) then it dosn't take as it can't compute hashCode of int. How to do this?
The idea of re-hashing doesn't involve calling hashCode for the hashCode of the key. It involves running some hardcoded function on the value obtained by key.hashCode().
For example, in Java 7 implementation of HashMap, the following function is used :
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
Then you use it with :
int hash = hash(key.hashCode());
int bucket = hash % buckets.length;
I am making a program that creates a generic hashtable that has both generic keys and generic data. When I cast the data array as the type Person, a class I created, and I try to access the data stored inside an individual Person from my Driver class using a getter method, I get an Object-type returned. My question is how do I access a non-generic class's information through a generic class, since it is stored in a generic array.
This is how I constructed the hashtable:
//region Instance Variables
private int count;
private K[] keys;
private E[] data;
private boolean[] hasBeenUsed;
//endregion
//region Constructor
public Table(int capacity)
{
if (capacity <= 0)
throw new IllegalArgumentException("Capacity is negative.");
keys = (K[]) new Object[capacity];
data = (E[]) new Object[capacity];
hasBeenUsed = new boolean[capacity];
}
My data getters(part of the Table class):
public E[] getData()
{
return data;
}
public E getDataAt(int index)
{
return data[index];
}
This is where I am trying to access the information from my Driver class:
public void print(Table hash)
{
Person[] people = toArray(hash);
for (int i = 0; i < hash.getData().length; i++)
{
if (null == hash.getKeyAt(i))
System.out.println("NULL AT " + i);
else
System.out.println("Key: " + hash.getKeyAt(i) + " Data: " + hash.getDataAt(i));
}
}
private Person[] toArray(Table hash)
{
Person[] people = new Person[hash.getData().length];
for (int i = 0; i < hash.getData().length; i++)
{
people[i] = hash.getDataAt(i);
}
}
This is my entire Hashtable Class if it's needed:
public class Table<K,E>
{
//region Instance Variables
private int count;
private K[] keys;
private E[] data;
private boolean[] hasBeenUsed;
//endregion
//region Constructors
/**
* Constructor
* Instantiates the keys, data, and hasBeenUsed variables with a passed value of capacity
* #param capacity the size to give the three instance arrays
*/
#SuppressWarnings("unchecked")
public Table(int capacity)
{
if (capacity <= 0)
throw new IllegalArgumentException("Capacity is negative.");
keys = (K[]) new Object[capacity];
data = (E[]) new Object[capacity];
hasBeenUsed = new boolean[capacity];
}
/**
* Constructor
* Default-Sets arrays to size 10
*/
#SuppressWarnings("unchecked")
public Table()
{
keys = (K[]) new Object[10];
data = (E[]) new Object[10];
hasBeenUsed = new boolean[10];
}
//endregion
//region Public Methods
/**
* Put
* Adds a new set to the table
* #param key The new Key value
* #param data the new Data value
* #return null if this is a new set, the old data value if the key already exists
*/
public E put(K key, E data)
{
int index = findIndex(key);
E answer;
if (index != -1)
{
answer = (E) this.data[index];
this.data[index] = data;
return answer;
} else if (count < this.data.length)
{
index = hash(key);
while (keys[index] != null)
{
System.out.println("Collision!");
index = nextIndex(index, key);
}
keys[index] = key;
this.data[index] = data;
hasBeenUsed[index] = true;
count++;
return null;
} else
System.out.println("ERROR IN PUT");
return null;
}
/**
* Remove
* Removes a key-data set from the table
* #return the value removed
*/
#SuppressWarnings("unchecked")
public E remove(K key)
{
int index = findIndex(key);
E answer = null;
if (index != -1)
{
answer = (E) data[index];
keys[index] = null;
data[index] = null;
count--;
}
return answer;
}
/**
* Contains Key
* Checks if the passed key exists
* #param key generic type key to check for
* #return true if the key exists
*/
public boolean containsKey(K key)
{
for (int i = 0; i < data.length; i++)
{
if (hasBeenUsed[i])
{
if (keys[i].equals(key))
{
return true;
}
}
}
return false;
}
/**
* Get
* Retrieves the data held stored with key
* #param key the key to access
* #return the data at key, null if key does not exist
*/
#SuppressWarnings("unchecked")
public E get(K key)
{
int index = findIndex(key);
if (index == -1)
return null;
else
return (E) data[index];
}
//endregion
//region Private Methods
//Locates the index value of key
private int findIndex(K key)
{
int count = 0;
int i = hash(key);
while ((count < data.length) && (hasBeenUsed[i]))
{
if (key.equals(keys[i]))
return i;
count++;
i = nextIndex(i, key);
}
return -1;
}
//Hashes the key
private int hash(K key)
{
return Math.abs(key.hashCode()) % data.length;
}
private int hash2(K key)
{
return 1 + (Math.abs(key.hashCode()) % (data.length-2));
}
//Determines if the next index is valid
private int nextIndex(int i, K key)
{
return (i + hash2(key)) % data.length;
}
//endregion
//region Getters and Setters
public int getCount()
{
return count;
}
public void setCount(int count)
{
this.count = count;
}
public K[] getKeys()
{
return keys;
}
public K getKeyAt(int index)
{
return keys[index];
}
public void setKeys(K[] keys)
{
this.keys = keys;
}
public E[] getData()
{
return data;
}
public E getDataAt(int index)
{
return data[index];
}
public void setData(E[] data)
{
this.data = data;
}
//endregion
}
EDIT
I edited the print method, and now I am getting a ClassCastException
Here is my new print method:
public void print(Table<Integer, Person> hash)
{
for (int i = 0; i < hash.getKeys().length; i++)
{
if (null == hash.getKeyAt(i))
System.out.println("NULL AT " + hash.getKeyAt(i));
else
{
System.out.println("Data at key " + hash.getKeyAt(i) + ": \n");
hash.getDataAt(i).printInfo();
}
}
}
You Table is actually generic. So when you want a new Table instance, you can say new Table<KEY_TYPE, VALUE_TYPE>() which will not need you to cast anywhere.
If you're not sure how to create an instance of generic class, please check https://docs.oracle.com/javase/tutorial/java/generics/types.html.