英文:
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("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.
答案1
得分: 1
你可能会考虑为什么java.util.Collection.toArray
有两个重载版本:
- 一个不带参数的版本,返回
Object[]
- 一个带有数组参数
T[]
的版本,并返回类型为T[]
的数组。
原因是无法构造泛型数组。如果可能的话,第二个方法就不是必需的。
你需要采取相同(或类似)的方法:将 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[]
、V
、Class<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:
- One taking no arguments, returns
Object[]
- One taking an array argument
T[]
, and returns an array of typeT[]
.
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论