Java 泛型数组类转换异常

huangapple go评论143阅读模式
英文:

Java Generic Array Class Cast Exception

问题

public V[] values() {
    V[] values = (V[]) new Object[size()];
    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;
    }
}
public class AVLTreeMap<K extends Comparable<K>, V> implements Serializable {
    // ... (other methods and classes)
}
private static class AVLTreeNode<K, V> {
    // ... (other fields and methods)
}
private static class DuplicateNode<V> {
    // ... (other fields and methods)
}
private AVLTreeMap<Integer, Integer> rankingMap;
// ...
System.out.println(rankingMap.values()[0]);

Please note that the provided code snippets are the translations of the relevant portions of your original code.

英文:

I'm creating a tree map class with the following method for obtaining an array of values:

@SuppressWarnings(&quot;unchecked&quot;)
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&lt;Integer, Integer&gt; rankingMap;
...
System.out.println(rankingMap.values()[0]); //Exception on this line

yeilds

Exception in thread &quot;main&quot; 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&lt;K extends Comparable&lt;K&gt; , V&gt; implements /*Map&lt;K, V&gt;,*/ Serializable{
    private AVLTreeNode&lt;K, V&gt; 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&lt;K, V&gt; insert(K key, V val, AVLTreeNode&lt;K, V&gt; currNode){
        //AVLTreeNode&lt;K, V&gt; node = new AVLTreeNode&lt;&gt;(key, val);
        if(currNode == null) {
            return new AVLTreeNode&lt;K, V&gt;(key, val);
        }else if(currNode.key.compareTo(key) &gt; 0){
            currNode.left = insert(key, val, currNode.left);
        }else if(currNode.key.compareTo(key) &lt; 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&lt;K, V&gt; node) {
        if(node.left == null &amp;&amp; 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&lt;K, V&gt; rotateRight(AVLTreeNode&lt;K, V&gt; node){
        AVLTreeNode&lt;K, V&gt; swapNode = node.left;
        node.left = swapNode.right;
        swapNode.right = node;
        updateNode(node);
        updateNode(swapNode);
        return swapNode;
    }

    private AVLTreeNode&lt;K, V&gt; rotateLeft(AVLTreeNode&lt;K, V&gt; node){
        AVLTreeNode&lt;K, V&gt; swapNode = node.right;
        node.right = swapNode.left;
        swapNode.left = node;
        updateNode(node);
        updateNode(swapNode);
        return swapNode;
    }

    private void updateNode(AVLTreeNode&lt;K, V&gt; node){
        if(node.left == null &amp;&amp; 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&lt;K, V&gt; rebalance(AVLTreeNode&lt;K, V&gt; node) {
        if (balanceFactor(node) &lt; -1) {
            if (balanceFactor(node.right) &gt; 0) {
                node.right = rotateRight(node.right);
            }
            node = rotateLeft(node);
        }
        else if (balanceFactor(node) &gt; 1) {
            if (balanceFactor(node.left) &lt; 0) {
                node.left = rotateLeft(node.left);
            }
            node = rotateRight(node);
        }
        return node;
    }

    public String tree(){
        return tree(root, 0, false);
    }

    private String tree(AVLTreeNode&lt;K, V&gt; node, int tabs, boolean left){
        String s = &quot;\n&quot;;
        for (int i = 0; i &lt; tabs - 1; i++) {
            s+=&quot;        &quot;;
        }
        s += &quot;----&quot;;
        if(node.left == null &amp;&amp; node.right == null){
            return s + &quot;|&quot;  + node.toString();
        }else if(node.left == null){
            return s + &quot;        |null&quot; + s + &quot;|&quot; +  node.toString() + &quot;|&quot; + tree(node.right, tabs + 1, false);
        }else if(node.right == null){
            return tree(node.left, tabs + 1, true) + s + &quot;|&quot;+ node.toString() + &quot;|&quot; + s+  &quot;        |null&quot;;
        }
        return tree(node.left, tabs + 1, true)  + s + &quot;|&quot;+ node.toString() +&quot;|&quot; + tree(node.right, tabs + 1, false);
    }

    @SuppressWarnings(&quot;unchecked&quot;)
    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&lt;K, V&gt; 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 &lt; node.dups; i++) {
                keys[idx++] = node.key;
            }
            keys = keys(node.right, idx, keys);
        }
        return keys;
    }

    @SuppressWarnings(&quot;unchecked&quot;)
    public V[] values() {
        V[] values = (V[]) new Object[size()];
        //System.out.println(values.getClass().getName());
        return values(root, 0, values);
    }

    private V[] values(AVLTreeNode&lt;K, V&gt; 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&lt;K, V&gt; node, V[] arr, int idx){
        DuplicateNode&lt;V&gt; dup = node.nextDup;
        for (int i = 0; i &lt; node.dups; i++) {
            arr[idx++] = dup.val;
            dup = dup.next;
        }
    }

    private static class AVLTreeNode&lt;K, V&gt;{
        public int height;
        public AVLTreeNode&lt;K, V&gt; left;
        public AVLTreeNode&lt;K, V&gt; right;
        public K key;
        public V val;
        public int dups;
        public int size;
        public DuplicateNode&lt;V&gt; 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&lt;V&gt; dup = new DuplicateNode&lt;&gt;(val);
            dup.next = nextDup;
            nextDup = dup;
            dups++;
            size++;
        }

        public String toString(){
            return key.toString();
        }
    }

    private static class DuplicateNode&lt;V&gt;{
        public V val;
        public DuplicateNode&lt;V&gt; next;

        public DuplicateNode(V val){
            this.val = val;
            next = null;
        }
    }
}

I get an array of the correct values but incorrect types.

答案1

得分: 1

你可能会考虑为什么java.util.Collection.toArray有两个重载版本:

原因是无法构造泛型数组。如果可能的话,第二个方法就不是必需的。

你需要采取相同(或类似)的方法:将 V[] 作为参数传递,以便用于构造数组实例:

public V[] values(V[] array) {
    // 基本上就是这样: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);
}

还有其他方法。例如,您可以传递一个 IntFunction<V[]>(或一个 V[]VClass<V>):

public V[] values(IntFunction<V[]> arraySupplier) {
    V[] values = arraySupplier.apply(size());

    //System.out.println(values.getClass().getName());
    return values(root, 0, values);
}

如果您不想将其传递给方法,可以将其传递给构造函数,并在 values() 方法中使用:

public AVLTreeMap(IntFunction<V[]> arraySupplier){
  this.arraySupplier = arraySupplier;  // 分配给字段
}

public V[] values() {
    V[] values = arraySupplier.apply(size());
    // ...
}

您应该根据构建和使用类的负担程度选择适合您的方法。

英文:

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&lt;V[]&gt; (or a V[], or a V, or a Class&lt;V&gt;):

public V[] values(IntFunction&lt;V[]&gt; 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&lt;V[]&gt; 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.

huangapple
  • 本文由 发表于 2020年5月31日 04:38:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/62108404.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定