I'm creating a tree map class with the following method for obtaining an array of values:
#SuppressWarnings("unchecked")
public V[] values() {
V[] values = (V[]) new Object[size()];
//System.out.println(values.getClass().getName());
return values(root, 0, values);
}
Now, when I try to access an element in this array I get a Class Cast Exception.
private AVLTreeMap<Integer, Integer> rankingMap;
...
System.out.println(rankingMap.values()[0]); //Exception on this line
yeilds
Exception in thread "main" java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.lang.Integer;
Could anyone explain why this is happening and how to fix it?
Thanks.
Edit: here is the entire tree map class
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
public class AVLTreeMap<K extends Comparable<K> , V> implements /*Map<K, V>,*/ Serializable{
private AVLTreeNode<K, V> root;
private int size;
public AVLTreeMap(){
root = null;
size = 0;
}
public int size(){
return size;
}
public void add(K key, V val){
root = insert(key, val, root);
size++;
}
private AVLTreeNode<K, V> insert(K key, V val, AVLTreeNode<K, V> currNode){
//AVLTreeNode<K, V> node = new AVLTreeNode<>(key, val);
if(currNode == null) {
return new AVLTreeNode<K, V>(key, val);
}else if(currNode.key.compareTo(key) > 0){
currNode.left = insert(key, val, currNode.left);
}else if(currNode.key.compareTo(key) < 0) {
currNode.right = insert(key, val, currNode.right);
}else if(currNode.key.compareTo(key) == 0) {
currNode.addDup(val);
}
updateNode(currNode);
return rebalance(currNode);
}
private int balanceFactor(AVLTreeNode<K, V> node) {
if(node.left == null && node.right == null){
return 0;
}else if(node.left == null){
return -node.right.height;
}else if(node.right == null){
return node.left.height;
}else {
return node.left.height - node.right.height;
}
}
private AVLTreeNode<K, V> rotateRight(AVLTreeNode<K, V> node){
AVLTreeNode<K, V> swapNode = node.left;
node.left = swapNode.right;
swapNode.right = node;
updateNode(node);
updateNode(swapNode);
return swapNode;
}
private AVLTreeNode<K, V> rotateLeft(AVLTreeNode<K, V> node){
AVLTreeNode<K, V> swapNode = node.right;
node.right = swapNode.left;
swapNode.left = node;
updateNode(node);
updateNode(swapNode);
return swapNode;
}
private void updateNode(AVLTreeNode<K, V> node){
if(node.left == null && node.right == null){
node.height = 1;
node.size = 1;
}else if(node.left == null){
node.height = node.right.height + 1;
node.size = node.right.size + 1;
}else if(node.right == null){
node.height = node.left.height + 1;
node.size = node.left.size + 1;
}else {
node.height = Math.max(node.left.height, node.right.height) + 1;
node.size = node.left.size + node.right.height + 1;
}
}
private AVLTreeNode<K, V> rebalance(AVLTreeNode<K, V> node) {
if (balanceFactor(node) < -1) {
if (balanceFactor(node.right) > 0) {
node.right = rotateRight(node.right);
}
node = rotateLeft(node);
}
else if (balanceFactor(node) > 1) {
if (balanceFactor(node.left) < 0) {
node.left = rotateLeft(node.left);
}
node = rotateRight(node);
}
return node;
}
public String tree(){
return tree(root, 0, false);
}
private String tree(AVLTreeNode<K, V> node, int tabs, boolean left){
String s = "\n";
for (int i = 0; i < tabs - 1; i++) {
s+=" ";
}
s += "----";
if(node.left == null && node.right == null){
return s + "|" + node.toString();
}else if(node.left == null){
return s + " |null" + s + "|" + node.toString() + "|" + tree(node.right, tabs + 1, false);
}else if(node.right == null){
return tree(node.left, tabs + 1, true) + s + "|"+ node.toString() + "|" + s+ " |null";
}
return tree(node.left, tabs + 1, true) + s + "|"+ node.toString() +"|" + tree(node.right, tabs + 1, false);
}
#SuppressWarnings("unchecked")
public K[] keys() {
K[] keys = (K[]) new Comparable[size()]; //https://stackoverflow.com/questions/34827626/cannot-be-cast-to-ljava-lang-comparable
return keys(root, 0, keys);
}
private K[] keys(AVLTreeNode<K, V> node, int idx, K[] keys){
if (node != null) {
keys = keys(node.left, idx, keys);
idx += (node.left != null) ? node.left.size : 0;
keys[idx++] = node.key;
for (int i = 0; i < node.dups; i++) {
keys[idx++] = node.key;
}
keys = keys(node.right, idx, keys);
}
return keys;
}
#SuppressWarnings("unchecked")
public V[] values() {
V[] values = (V[]) new Object[size()];
//System.out.println(values.getClass().getName());
return values(root, 0, values);
}
private V[] values(AVLTreeNode<K, V> node, int idx, V[] values){
if (node != null) {
values = values(node.left, idx, values);
idx += (node.left != null) ? node.left.size : 0;
values[idx++] = node.val;
addDuplicates(node, values, idx);
idx += node.dups;
values = values(node.right, idx, values);
}
return values;
}
private void addDuplicates(AVLTreeNode<K, V> node, V[] arr, int idx){
DuplicateNode<V> dup = node.nextDup;
for (int i = 0; i < node.dups; i++) {
arr[idx++] = dup.val;
dup = dup.next;
}
}
private static class AVLTreeNode<K, V>{
public int height;
public AVLTreeNode<K, V> left;
public AVLTreeNode<K, V> right;
public K key;
public V val;
public int dups;
public int size;
public DuplicateNode<V> nextDup;
public AVLTreeNode(K key, V val){
this.key = key;
this.val =val;
left = null;
right = null;
size = 1;
height = 1;
dups = 0;
nextDup = null;
}
public void addDup(V val){
DuplicateNode<V> dup = new DuplicateNode<>(val);
dup.next = nextDup;
nextDup = dup;
dups++;
size++;
}
public String toString(){
return key.toString();
}
}
private static class DuplicateNode<V>{
public V val;
public DuplicateNode<V> next;
public DuplicateNode(V val){
this.val = val;
next = null;
}
}
}
I get an array of the correct values but incorrect types.
You might value to consider why java.util.Collection.toArray has two overloads:
One taking no arguments, returns Object[]
One taking an array argument T[], and returns an array of type T[].
The reason is that it's not possible to construct a generic array. If it were possible, the second method wouldn't be necessary.
You need to take the same (or similar) approach: pass a V[] as a parameter that you can use to construct the array instance:
public V[] values(V[] array) {
// Basically, this: http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/classes/java/util/AbstractCollection.java#l176
V[] values = (V[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size());
//System.out.println(values.getClass().getName());
return values(root, 0, values);
}
There are alternatives. For example, you could pass a IntFunction<V[]> (or a V[], or a V, or a Class<V>):
public V[] values(IntFunction<V[]> arraySupplier) {
V[] values = arraySupplier.apply(size());
//System.out.println(values.getClass().getName());
return values(root, 0, values);
}
If you don't want to pass it to the method, you could pass it to the constructor, and use that in values():
public AVLTreeMap(IntFunction<V[]> arraySupplier){
this.arraySupplier = arraySupplier; // Assign to a field
}
public V[] values() {
V[] values = arraySupplier.apply(size());
// ...
}
You should choose your approach based on how burdensome it is to construct and use the class.
Related
When I use MapEntry[] entry = (MapEntry[]) new Object[capacity],
it tells me java.lang.ClassCastException.
How can this happen? I am confused about this. (Since it seems I should do the casting because it's generic)
I found some tutorial and they were using like this:
table = new Entry[capacity];
(http://www.javamadesoeasy.com/2015/02/hashmap-custom-implementation.html)
it did not even do the casting.
My codes are below.
public class MyHashMap<K, V> {
private class MapEntry {
K key;
V value;
MapEntry next;
MapEntry(K key, V value) {
this.key = key;
this.value = value;
}
}
private int size = 0;
private int capacity;
MapEntry[] entry;
#SuppressWarnings("unchecked")
MyHashMap() {
capacity = 10;
entry = (MapEntry[]) new Object[capacity];
}
#SuppressWarnings("unchecked")
MyHashMap(int capacity) {
entry = (MapEntry[]) new Object[capacity];
}
public void put(K key, V value) {
int hash = hashCode(key);
MapEntry newNode = new MapEntry(key, value);
if (entry[hash % capacity] == null) {
entry[hash % capacity] = newNode;
} else {
if (key == entry[hash % capacity].key) {
entry[hash % capacity].value = value;
} else {
MapEntry nextNode = entry[hash % capacity].next;
while (nextNode != null) {
if (key == nextNode.key) {
nextNode.value = value;
return;
}
nextNode = nextNode.next;
}
nextNode = newNode;
}
}
}
public V get(K key) {
int hash = hashCode(key);
MapEntry node = entry[hash % capacity];
if (node == null) {
return null;
}
if (node.key == key) {
return node.value;
}
while (key != node.key) {
node = node.next;
if (node.key == key) {
return node.value;
}
}
return null;
}
public boolean contains(K key) {
return get(key) != null;
}
public int size() {
return size;
}
public void remove(K key) {
int hash = hashCode(key);
MapEntry node = entry[hash % capacity];
if (node == null) return;
if (key == node.key) {
entry[hash % capacity] = node.next;
}
MapEntry pre = node;
while (key != node.key) {
node = node.next;
if (key == node.key) {
pre.next = node.next;
return;
}
pre = pre.next;
}
}
private int hashCode(K key) {
return Math.abs(key.hashCode());
}
public void display(){
for(int i = 0; i < capacity; i++){
if(entry[i] != null){
MapEntry node = entry[i];
while(node != null){
System.out.print("{" + node.key + "=" + node.value + "}" + " ");
node = node.next;
}
}
}
}
public static void main(String[] args) {
MyHashMap<Integer, Integer> hashMapCustom = new MyHashMap<Integer, Integer>();
hashMapCustom.put(21, 12);
hashMapCustom.put(25, 121);
hashMapCustom.put(30, 151);
hashMapCustom.put(33, 15);
hashMapCustom.put(35, 89);
System.out.println("value corresponding to key 21="
+ hashMapCustom.get(21));
System.out.println("value corresponding to key 51="
+ hashMapCustom.get(51));
System.out.print("Displaying : ");
hashMapCustom.display();
System.out.print("Displaying : ");
hashMapCustom.display();
}
}
You can't convert a class of an array by just casting that's yhe reason you get ClassCastException. You should use
`Arrays.copyof ().`
CustomType[]ca=Arrays.copyOf(array,array.length,CustomType[].class);
I have figured out how this work.
(Creation of array whose component type is either a type parameter, a concrete parameterized type or a bounded wildcard parameterized type, is type-unsafe.)
entry = (MapEntry[]) Array.newInstance(MapEntry.class, capacity);
In this way, there can be no errors.
There is another question with good solution.
How to create a generic array in Java?
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;
}
}
I am designing a Hashtable with linear probing collision resolution. I have designed a capacity which defines the number of buckets, and a load factor. When the number of elements in the hashtable exceeds the product of the capacity and the load factor, the array should be rehashed. For some reason, the method is not even being called in my code. Here is my main method:
public class MyJHUHashMap<K extends Comparable<? super K>, V> implements JHUHashMap<K, V>{
private int cap;
private int numElements;
private double loadFactor;
private Node<K,V>[] hashtable;
public static void main(String[] args) {
MyJHUHashMap<Integer, Integer> m = new MyJHUHashMap<>();
m.put(1, 1);
m.put(2, 2);
m.put(3, 3);
m.put(4, 4);
m.put(5, 5);
m.put(6, 6);
m.put(7, 7);
System.out.println(m.cap);
System.out.println(m.size());
for(int i=0; i<m.hashtable.length; i++) {
if(m.hashtable[i] != null)
System.out.println(m.hashtable[i].key);
}
}
and here is my code:
private final class Node<K extends Comparable<? super K>, V> {
public K key;
public V val;
public Node() {
this.key = null;
this.val = null;
}
public Node(K theKey, V theValue) {
this.key = theKey;
this.val = theValue;
}
}
public MyJHUHashMap() {
this.cap = 11;
this.numElements = 0;
this.loadFactor = 0.5;
this.hashtable = new Node[cap];
}
public MyJHUHashMap(int pInitCap, double pLoadFactor) {
if(pInitCap > 0 && pLoadFactor > 0) {
this.cap = pInitCap;
this.loadFactor = pLoadFactor;
} else {
throw new IllegalArgumentException();
}
this.numElements = 0;
this.hashtable = new Node[cap];
}
#Override
public V put(K key, V value) {
if(containsKey(key)) {
int foundindex = find(key);
V prevObject = this.hashtable[foundindex].val;
//hashtable[foundindex] = new Node(key, value);
this.hashtable[foundindex].val = value;
return prevObject;
}
int index = key.hashCode() % cap;
if(this.hashtable[index] == null) {
this.hashtable[index] = new Node<K,V>(key, value);
this.numElements++;
return null;
}
boolean found = false;
//index += 1;
while(found) {
index += 1;
if(hashtable[index] == null) {
hashtable[index] = new Node<K,V>(key, value);
this.numElements++;
found = true;
//return null;
}
//index += 1;
}
if(this.numElements > this.cap * this.loadFactor) {
System.out.println("Rehashing...");
rehashAndResize();
}
return null;
}
public void rehashAndResize() {
//Node<K,V>[] temp = new Node[this.hashtable.length*2];
//Node<K,V>[] temp = new Node[this.hashtable.length*2];
System.out.println("Calling rehash method");
MyJHUHashMap<K,V> temp = new MyJHUHashMap<K, V>(this.hashtable.length * 2, this.loadFactor);
for(int i=0; i<this.hashtable.length; i++) {
temp.put(this.hashtable[i].key, this.hashtable[i].val);
}
this.hashtable = temp.hashtable;
}
"Rehashing" and "Calling rehash method" are not being printed, so the method is not being called, which I can't fathom why. I input 7 elements, and the hashtable prints out 7 correctly when I ask it the number of elements, so why isn't the condition being evaluated?
EDIT: Ok, after closer inspection I found that the rehash method was never being reached because the method was returning early. Thank you for your help!
For an assignment, we were instructed to create a priority queue implemented via a binary heap, without using any built-in classes, and I have done so successfully by using an array to store the queued objects. However, I'm interested in learning how to implement another queue by using an actual tree structure, but in doing so I've run across a bit of a problem.
How would I keep track of the nodes on which I would perform insertion and deletion? I have tried using a linked list, which appends each node as it is inserted - new children are added starting from the first list node, and deleted from the opposite end. However, this falls apart when elements are rearranged in the tree, as children are added at the wrong position.
Edit: Perhaps I should clarify - I'm not sure how I would be able to find the last occupied and first unoccupied leaves. For example, I would always be able to tell the last inserted leaf, but if I were to delete it, how would I know which leaf to delete when I next remove the item? The same goes for inserting - how would I know which leaf to jump to next after the current leaf has both children accounted for?
A tree implementation of a binary heap uses a complete tree [or almost full tree: every level is full, except the deepest one].
You always 'know' which is the last occupied leaf - where you delete from [and modifying it is O(logn) after it changed so it is not a problem], and you always 'know' which is the first non-occupied leaf, in which you add elements to [and again, modifying it is also O(logn) after it changed].
The algorithm idea is simple:
insert: insert element to the first non-occupied leaf, and use heapify [sift up] to get this element to its correct place in the heap.
delete_min: replace the first element with the last occupied leaf, and remove the last occupied leaf. then, heapify [sift down] the heap.
EDIT: note that delete() can be done to any element, and not only the head, however - finding the element you want to replace with the last leaf will be O(n), which will make this op expensive. for this reason, the delete() method [besides the head], is usually not a part of the heap data structure.
I really wanted to do this for almost a decade.Finally sat down today and wrote it.Anyone who wants it can use it.I got inspired by Quora founder to relearn Heap.Apparently he was asked how would you find K near points in a set of n points in his Google phone screen.Apparently his answer was to use a Max Heap and to store K values and remove the maximum element after the size of the heap exceeds K.The approach is pretty simple and the worst case is nlog K which is better than n^2 in most sorting cases.Here is the code.
import java.util.ArrayList;
import java.util.List;
/**
* #author Harish R
*/
public class HeapPractise<T extends Comparable<T>> {
private List<T> heapList;
public List<T> getHeapList() {
return heapList;
}
public void setHeapList(List<T> heapList) {
this.heapList = heapList;
}
private int heapSize;
public HeapPractise() {
this.heapList = new ArrayList<>();
this.heapSize = heapList.size();
}
public void insert(T item) {
if (heapList.size() == 0) {
heapList.add(item);
} else {
siftUp(item);
}
}
public void siftUp(T item) {
heapList.add(item);
heapSize = heapList.size();
int currentIndex = heapSize - 1;
while (currentIndex > 0) {
int parentIndex = (int) Math.floor((currentIndex - 1) / 2);
T parentItem = heapList.get(parentIndex);
if (parentItem != null) {
if (item.compareTo(parentItem) > 0) {
heapList.set(parentIndex, item);
heapList.set(currentIndex, parentItem);
currentIndex = parentIndex;
continue;
}
}
break;
}
}
public T delete() {
if (heapList.size() == 0) {
return null;
}
if (heapList.size() == 1) {
T item = heapList.get(0);
heapList.remove(0);
return item;
}
return siftDown();
}
public T siftDown() {
T item = heapList.get(0);
T lastItem = heapList.get(heapList.size() - 1);
heapList.remove(heapList.size() - 1);
heapList.set(0, lastItem);
heapSize = heapList.size();
int currentIndex = 0;
while (currentIndex < heapSize) {
int leftIndex = (2 * currentIndex) + 1;
int rightIndex = (2 * currentIndex) + 2;
T leftItem = null;
T rightItem = null;
int currentLargestItemIndex = -1;
if (leftIndex <= heapSize - 1) {
leftItem = heapList.get(leftIndex);
}
if (rightIndex <= heapSize - 1) {
rightItem = heapList.get(rightIndex);
}
T currentLargestItem = null;
if (leftItem != null && rightItem != null) {
if (leftItem.compareTo(rightItem) >= 0) {
currentLargestItem = leftItem;
currentLargestItemIndex = leftIndex;
} else {
currentLargestItem = rightItem;
currentLargestItemIndex = rightIndex;
}
} else if (leftItem != null && rightItem == null) {
currentLargestItem = leftItem;
currentLargestItemIndex = leftIndex;
}
if (currentLargestItem != null) {
if (lastItem.compareTo(currentLargestItem) >= 0) {
break;
} else {
heapList.set(currentLargestItemIndex, lastItem);
heapList.set(currentIndex, currentLargestItem);
currentIndex = currentLargestItemIndex;
continue;
}
}
}
return item;
}
public static void main(String[] args) {
HeapPractise<Integer> heap = new HeapPractise<>();
for (int i = 0; i < 32; i++) {
heap.insert(i);
}
System.out.println(heap.getHeapList());
List<Node<Integer>> nodeArray = new ArrayList<>(heap.getHeapList()
.size());
for (int i = 0; i < heap.getHeapList().size(); i++) {
Integer heapElement = heap.getHeapList().get(i);
Node<Integer> node = new Node<Integer>(heapElement);
nodeArray.add(node);
}
for (int i = 0; i < nodeArray.size(); i++) {
int leftNodeIndex = (2 * i) + 1;
int rightNodeIndex = (2 * i) + 2;
Node<Integer> node = nodeArray.get(i);
if (leftNodeIndex <= heap.getHeapList().size() - 1) {
Node<Integer> leftNode = nodeArray.get(leftNodeIndex);
node.left = leftNode;
}
if (rightNodeIndex <= heap.getHeapList().size() - 1) {
Node<Integer> rightNode = nodeArray.get(rightNodeIndex);
node.right = rightNode;
}
}
BTreePrinter.printNode(nodeArray.get(0));
}
}
public class Node<T extends Comparable<?>> {
Node<T> left, right;
T data;
public Node(T data) {
this.data = data;
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class BTreePrinter {
public static <T extends Comparable<?>> void printNode(Node<T> root) {
int maxLevel = BTreePrinter.maxLevel(root);
printNodeInternal(Collections.singletonList(root), 1, maxLevel);
}
private static <T extends Comparable<?>> void printNodeInternal(
List<Node<T>> nodes, int level, int maxLevel) {
if (nodes.isEmpty() || BTreePrinter.isAllElementsNull(nodes))
return;
int floor = maxLevel - level;
int endgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0)));
int firstSpaces = (int) Math.pow(2, (floor)) - 1;
int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1;
BTreePrinter.printWhitespaces(firstSpaces);
List<Node<T>> newNodes = new ArrayList<Node<T>>();
for (Node<T> node : nodes) {
if (node != null) {
String nodeData = String.valueOf(node.data);
if (nodeData != null) {
if (nodeData.length() == 1) {
nodeData = "0" + nodeData;
}
}
System.out.print(nodeData);
newNodes.add(node.left);
newNodes.add(node.right);
} else {
newNodes.add(null);
newNodes.add(null);
System.out.print(" ");
}
BTreePrinter.printWhitespaces(betweenSpaces);
}
System.out.println("");
for (int i = 1; i <= endgeLines; i++) {
for (int j = 0; j < nodes.size(); j++) {
BTreePrinter.printWhitespaces(firstSpaces - i);
if (nodes.get(j) == null) {
BTreePrinter.printWhitespaces(endgeLines + endgeLines + i
+ 1);
continue;
}
if (nodes.get(j).left != null)
System.out.print("//");
else
BTreePrinter.printWhitespaces(1);
BTreePrinter.printWhitespaces(i + i - 1);
if (nodes.get(j).right != null)
System.out.print("\\\\");
else
BTreePrinter.printWhitespaces(1);
BTreePrinter.printWhitespaces(endgeLines + endgeLines - i);
}
System.out.println("");
}
printNodeInternal(newNodes, level + 1, maxLevel);
}
private static void printWhitespaces(int count) {
for (int i = 0; i < 2 * count; i++)
System.out.print(" ");
}
private static <T extends Comparable<?>> int maxLevel(Node<T> node) {
if (node == null)
return 0;
return Math.max(BTreePrinter.maxLevel(node.left),
BTreePrinter.maxLevel(node.right)) + 1;
}
private static <T> boolean isAllElementsNull(List<T> list) {
for (Object object : list) {
if (object != null)
return false;
}
return true;
}
}
Please note that BTreePrinter is a code I took somewhere in Stackoverflow long back and I modified to use with 2 digit numbers.It will be broken if we move to 3 digit numbers and it is only for simple understanding of how the Heap structure looks.A fix for 3 digit numbers is to keep everything as multiple of 3.
Also due credits to Sesh Venugopal for wonderful tutorial on Youtube on Heap data structure
public class PriorityQ<K extends Comparable<K>> {
private class TreeNode<T extends Comparable<T>> {
T val;
TreeNode<T> left, right, parent;
public String toString() {
return this.val.toString();
}
TreeNode(T v) {
this.val = v;
left = null;
right = null;
}
public TreeNode<T> insert(T val, int position) {
TreeNode<T> parent = findNode(position/2);
TreeNode<T> node = new TreeNode<T>(val);
if(position % 2 == 0) {
parent.left = node;
} else {
parent.right = node;
}
node.parent = parent;
heapify(node);
return node;
}
private void heapify(TreeNode<T> node) {
while(node.parent != null && (node.parent.val.compareTo(node.val) < 0)) {
T temp = node.val;
node.val = node.parent.val;
node.parent.val = temp;
node = node.parent;
}
}
private TreeNode<T> findNode(int pos) {
TreeNode<T> node = this;
int reversed = 1;
while(pos > 0) {
reversed <<= 1;
reversed |= (pos&1);
pos >>= 1;
}
reversed >>= 1;
while(reversed > 1) {
if((reversed & 1) == 0) {
node = node.left;
} else {
node = node.right;
}
reversed >>= 1;
}
return node;
}
public TreeNode<T> remove(int pos) {
if(pos <= 1) {
return null;
}
TreeNode<T> last = findNode(pos);
if(last.parent.right == last) {
last.parent.right = null;
} else {
last.parent.left = null;
}
this.val = last.val;
bubbleDown();
return null;
}
public void bubbleDown() {
TreeNode<T> node = this;
do {
TreeNode<T> left = node.left;
TreeNode<T> right = node.right;
if(left != null && right != null) {
T max = left.val.compareTo(right.val) > 0 ? left.val : right.val;
if(max.compareTo(node.val) > 0) {
if(left.val.equals(max)) {
left.val = node.val;
node.val = max;
node = left;
} else {
right.val = node.val;
node.val = max;
node = right;
}
} else {
break;
}
} else if(left != null) {
T max = left.val;
if(left.val.compareTo(node.val) > 0) {
left.val = node.val;
node.val = max;
node = left;
} else {
break;
}
} else {
break;
}
} while(true);
}
}
private TreeNode<K> root;
private int position;
PriorityQ(){
this.position = 1;
}
public void insert(K val) {
if(val == null) {
return;
}
if(root == null) {
this.position = 1;
root = new TreeNode<K>(val);
this.position++;
return ;
}
root.insert(val, position);
position++;
}
public K remove() {
if(root == null) {
return null;
}
K val = root.val;
root.remove(this.position-1);
this.position--;
if(position == 1) {
root = null;
}
return val;
}
public static void main(String[] args) {
PriorityQ<Integer> q = new PriorityQ<>();
System.out.println(q.remove());
q.insert(1);
q.insert(11);
q.insert(111);
q.insert(1111);
q.remove();
q.remove();
q.remove();
q.remove();
q.insert(2);
q.insert(4);
}
}
So I need a 2-dimensional ConcurrentHashMap.
It has to be as blazing fast as possible, as I'm going to be adding to and updating its values extremely frequently. It's in a multithreaded application, hence the choice to use ConcurrentHashMap instead of just HashMap.
Both the "x" and "y" indices are integers with a known range (0 through 40,000,000).
What I need to know is: What's the most efficient way to implement this so it'll be as speedy as possible? The most obvious route is to do a literal 2-D hashmap:
ConcurrentHashMap<Integer, ConcurrentHashMap<Integer, ValueObj>> foo;
Or I could make a private class "IntPair" with two properties x and y, and use that as a key... though if I do that, what's the most efficient way to do equals() and hashcode()? and will I wind up allocating too many new IntPairs? Could I keep a set of IntPairs for each x/y I've assigned, and then use a purely reflexive equals() such that I'm just checking for the exact same object instance?
Update:
Now that I've taken a closer look at Integer.valueOf(int), the specific caching model it uses wouldn't make sense here, since I'm dealing with a very sparse matrix with unpredictable entries. I really need to be caching all those IntPairs which are used, not a prespecified subset.
Intuitively, it seems to me that looking up an IntPair in a big map to see if I've already created it would, in fact, be more-or-less the same as just looking it up in the big "2-D" ConcurrentHashMap anyway, wouldn't it? So it seems the solution here is really to just use new IntPair(x,y) each time I look up a key. Yes?
It depends on how sparse your (x,y) points are, in the 40,000,000 x 40,000,000 matrix. My guess is that the matrix is going to be quite sparse anyway, so creating a lot of ConcurrentHashMaps is going to be expensive.
Your (immutable) IntPair suggestion seems more attractive in comparison. As you've suggested, you can even cache some of these pairs to improve performance (see Integer.valueOf(int) to see how this can be implemented using a static nested class and a static factory method). Since the hashcode will always be required, you can pre-compute it in the constructor and save it as a final field. To compute equals, you could use the identity equality for objects in the cache, otherwise you'll need to compare x and y individually.
EDIT: Here's the source code (OpenJDK) for Integer.valueOf(int).
ConcurrentHashMap is quite large, so you probably don't want a collection of them.
Short lived objects are actually very fast to allocate. Are you going to have to create the Integers anyway?
You could intern the coordinate objects, but the cost for just a lookup would probably be comparable to creating them anyway. The real win with Integer is that the same instances are shared when you keep around lots of them for some time.
If performance is really a huge issue, you could write (or use) a map-type object that maps longs to references. I wouldn't be surprised to see custom maps out there which also have functionality associated with coordinate systems (like finding nearest or within a range).
In response to Zach, Yes, the matrix will be very sparse.
I looked at the page you linked, and without a doubt the functionality of Integer.valueOf(int) would be ideal. If I developed a similar static method within my IntPair class, can I assume that I could define equals() to only check for strict reflexive equality?
That said, I don't see in that page where it explains how to implement that functionality using a static nested class and static factory method.... am I just missing it somehow? How do I do that?
Thanks!
I've made a Int2DMap implementation based on the standard Java HashMap. I find it is faster than using an IntPair as key. However it will need to be synchronized.
import java.io.*;
import java.util.*;
public class Int2DMap implements Map, Serializable {
private static final int DEFAULT_INITIAL_CAPACITY = 16;
private static final int MAXIMUM_CAPACITY = 1 << 30;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
protected Entry[] table;
protected int size;
protected int threshold;
protected float loadFactor;
protected transient volatile int modCount;
public Int2DMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
// Find a power of 2 >= initialCapacity
int capacity = 1;
while (capacity < initialCapacity) {
capacity <<= 1;
}
this.loadFactor = loadFactor;
this.threshold = (int) (capacity * loadFactor);
this.table = new Entry[capacity];
}
public Int2DMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public Int2DMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
public boolean containsKey(Object key) {
int[] xy = (int[]) key;
return containsKey(xy[0], xy[1]);
}
public Object get(Object key) {
int[] xy = (int[]) key;
return get(xy[0], xy[1]);
}
public Object put(Object key, Object value) {
int[] xy = (int[]) key;
return put(xy[0], xy[1], value);
}
public Object remove(Object key) {
int[] xy = (int[]) key;
return remove(xy[0], xy[1]);
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
protected static final int indexFor(int x, int y, int length) {
return (x * 31 + y) & (length - 1);
}
public Object get(int x, int y) {
for (Entry e = table[indexFor(x, y, table.length)]; e != null; e = e.next) {
if (e.x == x && e.y == y) {
return e.value;
}
}
return null;
}
public boolean containsKey(int x, int y) {
return getEntry(x, y) != null;
}
protected Entry getEntry(int x, int y) {
for (Entry e = table[indexFor(x, y, table.length)]; e != null; e = e.next) {
if (e.x == x && e.y == y) {
return e;
}
}
return null;
}
public Object put(int x, int y, Object value) {
int i = indexFor(x, y, table.length);
for (Entry e = table[i]; e != null; e = e.next) {
if (e.x == x && e.y == y) {
Object oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(x, y, value, i);
return null;
}
protected void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);
table = newTable;
threshold = (int) (newCapacity * loadFactor);
}
protected void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry e = src[j];
if (e != null) {
src[j] = null;
do {
Entry next = e.next;
int i = indexFor(e.x, e.y, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
public void putAll(Map m) {
int numKeysToBeAdded = m.size();
if (numKeysToBeAdded == 0) {
return;
}
if (numKeysToBeAdded > threshold) {
int targetCapacity = (int) (numKeysToBeAdded / loadFactor + 1);
if (targetCapacity > MAXIMUM_CAPACITY)
targetCapacity = MAXIMUM_CAPACITY;
int newCapacity = table.length;
while (newCapacity < targetCapacity)
newCapacity <<= 1;
if (newCapacity > table.length)
resize(newCapacity);
}
for (Iterator i = m.entrySet().iterator(); i.hasNext();) {
Map.Entry e = (Map.Entry) i.next();
put(e.getKey(), e.getValue());
}
}
public Object remove(int x, int y) {
Entry e = removeEntryForKey(x, y);
return (e == null ? null : e.value);
}
protected Entry removeEntryForKey(int x, int y) {
int i = indexFor(x, y, table.length);
Entry prev = table[i];
Entry e = prev;
while (e != null) {
Entry next = e.next;
Object k;
if (e.x == x && e.y == y) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
protected Entry removeMapping(Object o) {
if (!(o instanceof Entry))
return null;
Entry entry = (Entry) o;
int x = entry.x;
int y = entry.y;
int i = indexFor(x, y, table.length);
Entry prev = table[i];
Entry e = prev;
while (e != null) {
Entry next = e.next;
if (e.x == x && e.y == y) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
public void clear() {
modCount++;
Entry[] tab = table;
for (int i = 0; i < tab.length; i++)
tab[i] = null;
size = 0;
}
public boolean containsValue(Object value) {
Entry[] tab = table;
for (int i = 0; i < tab.length; i++)
for (Entry e = tab[i]; e != null; e = e.next)
if (value.equals(e.value))
return true;
return false;
}
static class Entry implements Map.Entry {
final int x;
final int y;
Object value;
Entry next;
Entry(int x, int y, Object value, Entry next) {
this.x = x;
this.y = y;
this.value = value;
this.next = next;
}
public final Object getKey() {
return new int[] { x, y };
}
public final Object getValue() {
return value;
}
public final Object setValue(Object newValue) {
Object oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry) o;
int[] xy = (int[])e.getKey();
if (x == xy[0] && y == xy[1]) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public final int hashCode() {
return ((31 + x) * 31 + y);
}
public final String toString() {
return "[" + x + ", " + y + "]=" + value;
}
/**
* This method is invoked whenever the value in an entry is overwritten by
* an invocation of put(k,v) for a key k that's already in the HashMap.
*/
void recordAccess(Int2DMap m) {
}
/**
* This method is invoked whenever the entry is removed from the table.
*/
void recordRemoval(Int2DMap m) {
}
}
void addEntry(int x, int y, Object value, int bucketIndex) {
Entry e = table[bucketIndex];
table[bucketIndex] = new Entry(x, y, value, e);
if (size++ >= threshold)
resize(2 * table.length);
}
private abstract class HashIterator implements Iterator {
Entry next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
Entry current; // current entry
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
public final boolean hasNext() {
return next != null;
}
final Entry nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry e = current = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
return e;
}
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
int x = current.x;
int y = current.y;
current = null;
Int2DMap.this.removeEntryForKey(x, y);
expectedModCount = modCount;
}
}
private final class ValueIterator extends HashIterator {
public Object next() {
return nextEntry().value;
}
}
private final class KeyIterator extends HashIterator {
public Object next() {
return nextEntry().getKey();
}
}
private final class EntryIterator extends HashIterator {
public Map.Entry next() {
return nextEntry();
}
}
// Subclass overrides these to alter behavior of views' iterator() method
Iterator newKeyIterator() {
return new KeyIterator();
}
Iterator newValueIterator() {
return new ValueIterator();
}
Iterator newEntryIterator() {
return new EntryIterator();
}
public Set keySet() {
return new KeySet();
}
private final class KeySet extends AbstractSet {
public Iterator iterator() {
return newKeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
int[] xy = (int[]) o;
return Int2DMap.this.removeEntryForKey(xy[0], xy[1]) != null;
}
public void clear() {
Int2DMap.this.clear();
}
}
public Collection values() {
return new Values();
}
private final class Values extends AbstractCollection {
public Iterator iterator() {
return newValueIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
Int2DMap.this.clear();
}
}
public Set entrySet() {
return new EntrySet();
}
private final class EntrySet extends AbstractSet {
public Iterator iterator() {
return newEntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Entry e = (Entry) o;
Entry candidate = getEntry(e.x, e.y);
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
return removeMapping(o) != null;
}
public int size() {
return size;
}
public void clear() {
Int2DMap.this.clear();
}
}
public static void main(String[] args) {
try {
Int2DMap map = new Int2DMap();
map.put(20, 6000, "Test");
System.out.println(map.size() == 1);
System.out.println(map.get(20, 6000) != null);
System.out.println("Test".equals(map.get(20, 6000)));
for (Iterator iter = map.values().iterator(); iter.hasNext();) {
System.out.println("Test".equals(iter.next()));
}
for (Iterator iter = map.keySet().iterator(); iter.hasNext();) {
int[] key = (int[])iter.next();
System.out.println(key[0] == 20 && key[1] == 6000);
}
for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
Map.Entry e = (Map.Entry)iter.next();
System.out.println(e.toString().equals("[20, 6000]=Test"));
}
map.remove(20, 6000);
System.out.println(map.size() == 0 && map.get(20, 6000) == null);
long start = System.nanoTime();
int max = 40000000;
for (int i = 0; i < 500000; i++) {
int x = (int)(Math.random() * max);
int y = (int)(Math.random() * max);
map.put(x, y, "");
int x2 = (int)(Math.random() * max);
int y2 = (int)(Math.random() * max);
Object o = map.get(x2, y2);
}
System.out.println(map.size());
System.out.println((System.nanoTime() - start) / 1000000);
Map map2 = new HashMap();
start = System.nanoTime();
for (int i = 0; i < 500000; i++) {
String key = "" + (int)(Math.random() * max) + "," + (int)(Math.random() * max);
map2.put(key, "");
String key2 = "" + (int)(Math.random() * max) + "," + (int)(Math.random() * max);
Object o = map2.get(key2);
}
System.out.println(map2.size());
System.out.println((System.nanoTime() - start) / 1000000);
} catch (Throwable t) {
t.printStackTrace();
}
}
}