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?
Related
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( ));
}
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
I was able to write my own Binary Search Tree, but I am having a lot of trouble figuring out how to turn that into a Balanced Binary Search tree.
Could someone help me implement a Balanced binary search tree code with the regular binary tree.
I think I was successful in changing my TreeNode class to have the necessary changes.
I added another key and another value along with another TreeNode middle to hold the middle pointer when you get to a 3 node in the tree.
I then added another constructor to hold the case if it was a 3 node. I believe I did this right.
public class TreeNode<V>
{
public int key;
public int key1;
public V value;
public V value1;
public TreeNode<V> left;
public TreeNode<V> right;
public TreeNode<V> middle;
public TreeNode(int key, V value)
{
this.key = key;
this.value = value;
this.left = null;
this.right = null;
}
public TreeNode(int key, V value, int key1, V value1)
{
this.key = key;
this.key1 = key1;
this.value = value;
this.value1 = value1;
this.left = null;
this.right = null;
this.middle = null;
}
The tough part comes to when I need to change the actual BST Class. I know the put is going to change quite a bit because we have to check and see if it is a 2 node or a 3 node, as well as check for what the parent node is.
Here is what I have so far:
public class BST<V>
{
private TreeNode<V> root;
public BST()
{
this.root = null;
}
public V get(int key)
{
return get(root, key);
}
private V get(TreeNode<V> current, int key)
{
if (current == null)
return null;
else if (key == current.key)
return current.value;
else if (key < current.key)
return get(current.left, key);
else
return get(current.right, key);
}
public void put(int key, V value)
{
if (root == null)
root = new TreeNode<>(key, value);
else
put(root, key, value);
}
private void put(TreeNode<V> current, int key, V value)
{
if (key == current.key)
{
current.value = value;
return;
}
else if (key < current.key)
{
if (current.left == null)
{
current.left = new TreeNode<>(key, value);
return;
}
else
put(current.left, key, value);
}
else
{
if (current.right == null)
{
current.right = new TreeNode<>(key, value);
return;
}
else
put(current.right, key, value);
}
}
}
My difficultly comes most with the recursion. I understand how basic recursion works, but using it to implement a balanced binary search tree is seeming a much more difficult talk than originally thought.
You only want a binary search tree, correct? If so, there isn't really a need for keys (Which are used for M-ary trees).
This isn't exactly an answer, but hopefully this will help simplify your code at least a little bit.
I am supposed to create a Binary Search Tree or B-Tree with a maximum of three nodes. Any one "parent" or "child" node can hold 1 or 2 values making it so that it can either have 2 or 3 "child" pointers on it.
For example: This is okay because there is only a maximum of 2 values in each set
23 30
/ | \
12 25 [35 38]
Where as this is not: because the root has 3 values in it and 4 "child" nodes
12 15 18
/ | | \
4 14 16 20
I was able to write my own Binary Search Tree, but I am having a lot of trouble figuring out how to turn that into a 2node or 3node Binary Search tree.
Could someone help me implement a 2node or 3node binary search tree code with the regular binary tree.
public class TreeNode<K extends Comparable<K>, V>
{
public K key;
public V value;
public TreeNode<K,V> left;
public TreeNode<K,V> right;
public TreeNode(K key, V value)
{
this.key = key;
this.value = value;
this.left = null;
this.right = null;
}
}
The tough part comes to when I need to change the actual BST Class. I know the put is going to change quite a bit because we have to check and see if it is a 2 node or a 3 node.
Here is what I have so far:
public class BST<K extends Comparable<K>, V>
{
private TreeNode<K, V> root;
public BST()
{
this.root = null;
}
public void put(K key, V value)
{
if (root == null)
root = new TreeNode<>(key, value);
else
put(root, key, value);
}
private void put(TreeNode<K, V> current, K key, V value)
{
int n = key.compareTo(current.key);
if (n == 0)
{
current.value = value;
return;
}
else if (n < 0)
{
if (current.left == null)
{
current.left = new TreeNode<>(key, value);
return;
}
else
put(current.left, key, value);
}
else
{
if (current.right == null)
{
current.right = new TreeNode<>(key, value);
return;
}
else
put(current.right, key, value);
}
}
public V get(K key)
{
return get(root, key);
}
private V get(TreeNode<K,V> current, K key)
{
if (current == null)
return null;
else
{
int n = key.compareTo(current.key);
if (n==0)
return current.value;
else if (n < 0)
return get(current.left, key);
else
return get(current.right, key);
}
}
public int size()
{
return size(root);
}
private int size(TreeNode<K,V> current)
{
if(current == null)
return 0;
else
return size(current.left) + size(current.right) + 1;
}
public int depth()
{
return depth(root) - 1;
}
private int depth(TreeNode<K,V> current)
{
if(current == null)
return 0;
else
return Math.max(depth(current.left), depth(current.right)) + 1;
}
}
In the documentations, the return type for the HashMap or Hashtable is the value itself as following,
public V put(K key, V value)
and, it tells about the return type is the previous value associated with key, or null if there was no mapping for key. (A null return can also indicate that the map previously associated null with key.)
Say, if I write a Hashtable as following,
Hashtable<Integer, Character> hash = new Hashtable<Integer, Character>();
System.out.println(hash.put(12, 'c'));
Now, the above code returns null. I'm implementing a Hashtable and I'm slightly confused when to return null or V where V will be the previous value associated with key. My code is as following,
// for dealing w/ the table data
class HashEntry<K, V> {
private final K key;
private V value;
public HashEntry(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
class MyHashTable <K, V> {
private int size;
private int DEFAULT_CAPACITY = 16;
private HashEntry<K, V>[] values = new HashEntry[DEFAULT_CAPACITY];
public V put(K key, V value) {
boolean insert = true;
for (int i = 0; i < size; i++) {
if (values[i].getKey().equals(key)) {
values[i].setValue(value);
insert = false;
}
}
if (insert) {
// method for check or increase capacity
increaseCapacity();
values[size++] = new HashEntry<K, V>(key, value);
}
return null;
// return value;
}
When should I return null or value ( type V) in the implementation ?
In your example this is your first time assigning a value to the key 12 so as stated in the documentation null is returned since there was no previous value stored for that key.
If you then did:
System.out.println(hash.put(12, 'd'));
'c' would be printed because it is returned as the previous value for the key 12.
Edit based on additional info in question:
Here is a possible implementation of your put method to return the previous value correctly.
public V put(K key, V value) {
boolean insert = true;
V prevValue = null;
for (int i = 0; i < size; i++) {
if (values[i].getKey().equals(key)) {
prevValue = values[i].getValue();
values[i].setValue(value);
insert = false;
}
}
if (insert) {
// method for check or increase capacity
increaseCapacity();
values[size++] = new HashEntry<K, V>(key, value);
}
return prevValue;
}