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?
Related
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.
I made my own implementation of Java's HashMap after looking at the source code and watching a fiew videos on it. I only added the key methods, and this is what I came up with:
public class MyHashMap {
private Node[] arr;
private int n;
public MyHashMap() {
n = 16;
arr = new Node[n];
}
public void put(int key, int value) {
int index = key & (n - 1);
if (arr[index] == null) {
arr[index] = new Node(key, value);
} else {
Node node = arr[index];
if(node.key == key) {
node.value = value;
return;
}
while (node.next != null) {
if(node.key == key) {
node.value = value;
return;
}
node = node.next;
}
node.next = new Node(key, value);
}
}
public int get(int key) {
int index = key & (n - 1);
if(arr[index] == null) {
return -1;
} else {
Node node = arr[index];
while(node != null) {
if(node.key == key) {
return node.value;
}
}
return -1;
}
}
public void remove(int key) {
int index = key & (n - 1);
if(arr[index] == null) {
return;
} else {
Node node = arr[index];
if(node.next == null && node.key == key) {
arr[index] = null;
return;
}
while(node.next != null) {
if(node.key == key) {
node.next = node.next.next;
return;
}
}
}
}
private class Node {
int key, value;
Node next;
Node(int key, int value) {
this.key = key;
this.value = value;
}
}
}
I can do relatively small commands without it taking to long, like this:
MyHashMap hashMap = new MyHashMap();
hashMap.put(1, 1);
hashMap.put(2, 2);
System.out.println(hashMap.get(1)); // returns 1
System.out.println(hashMap.get(3)); // returns -1 (not found)
hashMap.put(2, 1); // update the existing value
System.out.println(hashMap.get(2)); // returns 1
hashMap.remove(2); // remove the mapping for 2
System.out.println(hashMap.get(2)); // returns -1 (not found)
But as soon as I do more things, it takes a much longer time. I tried to see if Java's HashMap was as slow, but it took barely any time. What makes my implementation so much slower? It's not just a few milliseconds, it's about 5 seconds slower when my commands aren't even that much.
The error I believe starts on line 102: int treeDepth(Node Node) because when I run the code with a regular while loop with a count, it runs and displays a tree. But as soon as I change the while condition to while (treeDepth(this.root) <= 5) it runs but displays nothing, and I get no errors. Trying to make it so the tree that is created doesn't have a depth larger than 5.
import java.io.*;
import java.util.*;
class Node {
int value;
Node left;
Node right;
Node(int value) {
this.value = value;
right = null;
left = null;
}
}
public class treeStructureBinary{
Node root;
public static void main(String[] args) {
treeStructureBinary bn =new treeStructureBinary();
bn.appMain(args);
}
void appMain(String[] args) {
createBinaryTree();
}
private Node addRecursive(Node current, int value) {
if (current == null) {
return new Node(value);
}
if (value < current.value) {
current.left = addRecursive(current.left, value);
} else if (value > current.value) {
current.right = addRecursive(current.right, value);
} else {
return current;
}
return current;
}
public void add(int value) {
this.root = addRecursive(this.root, value);
}
public treeStructureBinary createBinaryTree() {
treeStructureBinary bt = new treeStructureBinary();
int [] array = new int[89];
int counter = 0;
boolean check = true;
while (treeDepth(this.root) <= 5)
{
Random rand = new Random();
int n = rand.nextInt(89) + 10;
for(int z = 0; z <= counter; z++)
{
if ( n == array[z])
{
check = false;
break;
}
}
if (check == true)
{
bt.add(n);
array[counter] = n;
counter++;
}
check = true;
}
bt.traverseLevelOrder();
return bt;
}
public void traverseLevelOrder() {
if (this.root == null) {
return;
}
Queue<Node> nodes = new LinkedList<>();
nodes.add(this.root);
while (!nodes.isEmpty()) {
Node node = nodes.remove();
System.out.print(" " + node.value);
if (node.left != null) {
nodes.add(node.left);
}
if (node.right != null) {
nodes.add(node.right);
}
}
}
int treeDepth(Node Node){
if (Node == null) {
return 0;
}else {
int lDepth = treeDepth(Node.left);
int rDepth = treeDepth(Node.right);
if (lDepth > rDepth) {
System.out.println("lDepth" + "\n");
return (lDepth + 1);
}else {
System.out.println("rDepth" + "\n");
return (rDepth + 1);
}
}
}
}
I think your addRecursive never actually adds the node to the tree--or always adds it? Anyway it looks funky. I'd focus on that for a bit.
This code in particular:
if (value < current.value) {
current.left = addRecursive(current.left, value);
} else if (value > current.value) {
current.right = addRecursive(current.right, value);
} else {
return current;
}
always forces an assign (even if it's not a leaf) and the final else will only execute when value == current.value which is probably not what you want.
I don't really want to go much further because it looks homeworky and you'll gain more figuring it out yourself.
It might work anyway (You just may be re-assigning every node at every level) but I'm not sure without running it.
Anyway, if this is a homework assignment I'd really like to commend you on your style, it's one of the best I've seen posted here for a homework-like question.
Main problem here is that you are working on two different trees.
First you create one tree in main function:
public static void main(String[] args) {
treeStructureBinary bn =new treeStructureBinary();
bn.appMain(args);
}
Then you create another one in createBinaryTree method:
public SthApplication createBinaryTree() {
treeStructureBinary bt = new treeStructureBinary();
See, you used new keyword twice, so there will be two objects.
Later in your app you refer to this.root (which is the one from main), but some methods use local variable bt.
In example, treeDepth(this.root) operates on different tree then the bt.add(n), so it goes into infinite loop.
If you solve that problem, you will know how to finish the rest.
Thanks guys I figured it out!
import java.io.*;
import java.util.*;
class Node {
int value;
int balancefactor;
int nodex;
Node left;
Node right;
Node(int value, int balancefactor, int nodex) {
this.value = value;
this.balancefactor = balancefactor;
this.nodex = nodex;
this.right = null;
this.left = null;
}
}
public class treeStructureBinary{
Node root;
public static void main(String[] args) {
treeStructureBinary bn =new treeStructureBinary();
bn.appMain(args);
}
void appMain(String[] args) {
int count = args.length;
if (count >1) {
count = 1;
}
String [] cmdln = {""};
for (int i=0;i<count;i++) {
cmdln[i]=args[i];
}
if (cmdln[0].equals("BT")){
createBinaryTree();
} else if (cmdln[0].equals("AVL")) {
} else {
System.out.println("Please enter BT or AVL to choose the type of
tree.");
}
}
private Node addRecursive(Node current, int value, int balancefactor, int
nodex) {
if (current == null) {
return new Node(value, balancefactor, nodex);
} if (value < current.value) {
balancefactor++;
nodex=(nodex*2);
current.left = addRecursive(current.left, value, balancefactor,
nodex);
} else if (value > current.value) {
balancefactor++;
nodex=(nodex*2)+1;
current.right = addRecursive(current.right, value, balancefactor,
nodex);
} else {
return current;
}
return current;
}
public void add(int value) {
int balancefactor=1;
int nodex=0;
this.root = addRecursive(this.root, value, balancefactor, nodex);
}
public treeStructureBinary createBinaryTree() {
treeStructureBinary bt = new treeStructureBinary();
int [] array = new int[89];
int counter = 0;
boolean check = true;
int temp = 0;
while (temp < 5) {
Random rand = new Random();
int n = rand.nextInt(89) + 10;
for(int z = 0; z <= counter; z++) {
if ( n == array[z]) {
check = false;
break;
}
}
if (check == true) {
bt.add(n);
array[counter] = n;
counter++;
}
check = true;
temp = bt.treeDepth();
}
bt.traverseLevelOrder();
Scanner reader =new Scanner(System.in);
System.out.println("\n\nEnter a number to delete or 0 to exit");
int input = reader.nextInt();
Boolean isMatch = true;
while (input!=0) {
for(int p = 0; p < counter; p++)
{
//System.out.println(array[p]);
if (input != array[p])
{
isMatch = false;
}
else
{
isMatch = true;
array[p] = 0;
break;
}
}
if (isMatch == false )
{
System.out.println("Error, number not found.");
}
bt.nodeDelete(input);
bt.traverseLevelOrder();
System.out.println("\n\nEnter a number to delete or 0 to exit");
input = reader.nextInt();
}
return bt;
}
public void traverseLevelOrder() {
int count = 0;
int outer = 31;
int inner = 30;
int lastnode= 0;
int check = 0;
if (this.root == null) {
return;
}
Queue<Node> nodes = new LinkedList<>();
nodes.add(this.root);
while (!nodes.isEmpty()) {
Node node = nodes.remove();
if (count < node.balancefactor) {
System.out.print("\n");
for (int i=0; i<outer; i++) {
System.out.print(" ");
}
inner=outer;
outer=outer/2;
count++;
lastnode=0;
check=0;
}
check=((node.nodex-lastnode));
for (int i=0; i<(inner*check*2);i++) {
System.out.print(" ");
}
if (check >1) {
for (int j=0;j<check;j++) {
System.out.print(" ");
}
}
lastnode=node.nodex;
System.out.print(node.value);
if (node.left != null) {
nodes.add(node.left);
}
if (node.right != null) {
if (node.right==null &&lastnode == 0) {
if (count==5) {
break;
}
System.out.print(" ");
}
nodes.add(node.right);
}
}
}
int treeDepth(){
int temp = treeDepthRecursive(this.root);
return temp;
}
int treeDepthRecursive(Node current) {
if (current == null) {
return 0;
} else {
int lDepth = treeDepthRecursive(current.left);
int rDepth = treeDepthRecursive(current.right);
if (lDepth > rDepth) {
return (lDepth + 1);
} else {
return (rDepth + 1);
}
}
}
public void nodeDelete(int value) {
nodeDeleteRecursive(root, value);
}
public Node nodeDeleteRecursive(Node current, int value) {
if (current == null) {
return null;
}
if (value == current.value) {
if (current.left ==null && current.right==null) {
return null;
}
if (current.right==null) {
return current.left;
}
if (current.left==null) {
return current.right;
}
int sValue = findSmall(current.right);
current.value = sValue;
current.right = nodeDeleteRecursive(current.right, sValue);
return current;
}
if (value < current.value) {
current.left = nodeDeleteRecursive(current.left, value);
return current;
}
current.right =nodeDeleteRecursive(current.right, value);
return current;
}
public int findSmall(Node root) {
return root.left == null?(root.value):(findSmall(root.left));
}
}
I am working on a Binary Search Tree algorithm (called BST in the code) and whenever I run the program it just keeps running for a long time. I know this means that there is an infinite loop but I can't figure out what/where is the problem (I've been trying for a while). I've had this problem once before and never figured that out either. If anyone could help find out where the loop is, what to change it to and also explain why it causes it I would be extremely grateful for the knowledge as it would also help me for future endeavors. Here is the code:
import java.util.Queue;
import java.util.ArrayDeque;
public class BST<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
public Node(Key key, Value val, int N) {
this.key = key;
this.val = val;
this.N = N;
}
}
// is the symbol table empty?
public boolean isEmpty() {
return size() == 0;
}
// return number of key-value pairs in BST
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;
}
/***********************************************************************
* Search BST for given key, and return associated value if found,
* return null if not found
***********************************************************************/
// does there exist a key-value pair with given key?
public boolean contains(Key key) {
return get(key) != null;
}
// return value associated with the given key, or null if no such key exists
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;
}
/***********************************************************************
* Insert key-value pair into BST
* If key already exists, update with new value
***********************************************************************/
public void put(Key key, Value val) {
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);
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 x.val = val;
x.N = 1 + size(x.left) + size(x.right);
return x;
}
/***********************************************************************
* Delete
***********************************************************************/
public void deleteMin() {
if (isEmpty()) throw new RuntimeException("Symbol table 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;
}
public void deleteMax() {
if (isEmpty()) throw new RuntimeException("Symbol table 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;
}
public void delete(Key key) {
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;
}
/***********************************************************************
* Min, max, floor, and ceiling
***********************************************************************/
public Key min() {
if (isEmpty()) return null;
return min(root).key;
}
private Node min(Node x) {
if (x.left == null) return x;
else return min(x.left);
}
public Key max() {
if (isEmpty()) return null;
return max(root).key;
}
private Node max(Node x) {
if (x.right == null) return x;
else return max(x.right);
}
public Key floor(Key key) {
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;
}
public Key ceiling(Key key) {
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);
}
/***********************************************************************
* Rank and selection
***********************************************************************/
public Key select(int k) {
if (k < 0 || k >= size()) return null;
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;
}
public int rank(Key key) {
return rank(key, root);
}
// Number of keys in the subtree less than x.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);
}
/***********************************************************************
* Range count and range search.
***********************************************************************/
public Iterable<Key> keys() {
return keys(min(), max());
}
public Iterable<Key> keys(Key lo, Key hi) {
Queue<Key> queue = new ArrayDeque<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.offer(x.key);
if (cmphi > 0) keys(x.right, queue, lo, hi);
}
public int size(Key lo, Key hi) {
if (lo.compareTo(hi) > 0) return 0;
if (contains(hi)) return rank(hi) - rank(lo) + 1;
else return rank(hi) - rank(lo);
}
// height of this BST (one-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));
}
/*************************************************************************
* Check integrity of BST data structure
*************************************************************************/
private boolean check() {
if (!isBST()) System.out.println("Not in symmetric order");
if (!isSizeConsistent()) System.out.println("Subtree counts not consistent");
if (!isRankConsistent()) System.out.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 tree 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;
}
/*****************************************************************************
* Test client
*****************************************************************************/
public static void main(String[] args) {
BST<String, Integer> st = new BST<String, Integer>();
for (int i = 0; !System.out.equals(i); i++) {
String key = System.out.toString();
st.put(key, i);
}
for (String s : st.keys())
System.out.println(s + " " + st.get(s));
}
}
Thank you in advance to anyone who ofers help!
for (int i = 0; !System.out.equals(i); i++)
system out won't be ever equals an integer
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();
}
}
}