0

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.

3
  • Assuming that <V extends Comparable<V>> change V[] values = (V[]) new Object[size()] with V[] values = (V[]) new Comparable[size()] Commented May 30, 2020 at 20:43
  • 6
    Could you post the whole class? Commented May 30, 2020 at 20:44
  • stackoverflow.com/questions/529085/… .. check this for solution. Commented May 30, 2020 at 20:56

1 Answer 1

1

You might value to consider why java.util.Collection.toArray has two overloads:

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.