Я пытаюсь создать массив вложенного класса Key, который использует переменные/параметры типа из основного класса BTree, но не могу избавиться от ClassCastException во время выполнения. Я не очень хорошо разбираюсь в дженериках в Java, я был бы признателен, если бы кто-нибудь сообщил мне, в чем проблема и как ее исправить.
public class BTree<T extends Comparable<T>, V> {
//...
private class Node {
public int n;
public boolean isLeaf = false;
public Key[] keys = (Key[]) new Comparable[2 * MIN_DEGREE - 1]; //ClassCastException
public Node[] children = (Node[]) new Object[2 * MIN_DEGREE];
}
private class Key implements Comparable<Key> {
public T key;
public V val;
public Key(T key, V val) {
this.key = key;
this.val = val;
}
public boolean lessThan(Key that) {
return this.key.compareTo(that.key) < 0;
}
public boolean greaterThan(Key that) {
return this.key.compareTo(that.key) > 0;
}
@Override
public int compareTo(Key that) {
if (this.lessThan(that)) return -1;
if (this.greaterThan(that)) return 1;
return 0;
}
}
//....
}
Редактировать:
Я также пробовал преобразовать массив Object в массив Key, и он также выдает ClassCastException:
public Key[] keys = (Key[]) new Object[2 * MIN_DEGREE - 1];
И когда я создаю массив Key без приведения, при компиляции выдает ошибку Generic array creation:
public Key[] keys = new Key[2 * MIN_DEGREE - 1];
@RealSkeptic Я получаю общую ошибку создания массива, когда создаю массив ключей new Key[size]
Да, потому что дженерики и массивы несовместимы. Я не уверен, что вы пытаетесь здесь сделать, но почему вы не используете списки?
Ошибки создания универсального массива можно решить, создав массив объектов и приведя его к запрошенному универсальному типу. например T[] myArray = (T[]) новый объект[10]; Также проверьте другие обсуждения по этому поводу, например. stackoverflow.com/questions/17831896/…
@RealSkeptic Я реализую B-деревья из книги CLRS, и в ней используются массивы, также я думаю, что использование списков может повлиять на время выполнения (хотя не уверен) таких операций, как поиск и вставка.




РЕДАКТИРОВАТЬ: полная перезапись
Как насчет извлечения приведения внутреннего класса к типу-оболочке, имеющего внутренний класс в качестве параметра универсального типа? Другой ответ предлагает коллекции, которые в любом случае имеют вспомогательные объекты. Если мы определим такую оболочку массива, то единственный недостаток в том, что когда массив читается или записывается, это происходит через методы, а не напрямую:
public class Outer<A,B> {
static final int SIZE = 10;
public Outer() {
Inner1 innerInstance = new Inner1();
}
private class Inner1 {
//Inner2[] inner2array = new Inner2[SIZE];
Array<Inner2> inner2array = new Array<>(SIZE);
}
private class Inner2 {
A a;
B b;
}
public static void main(String[] test) {
Outer outerInstance = new Outer();
}
private static class Array<T> {
private Object[] values;
public Array(int size) {
values = new Object[size];
}
@SuppressWarnings("unchecked")
public T get(int index) {
return (T) values[index];
}
public void set(int index, T value) {
values[index] = value;
}
public int length() {
return values.length;
}
}
}
Итак, это:
public Key[] keys = new Key[2 * MIN_DEGREE - 1];
Становится:
public Array<Key> keys = new Array<>(2 * MIN_DEGREE - 1);
Конечно, его можно улучшить, например сделать итерируемым, поэтому циклы foreach тоже будут работать:
@Override // implements Iterable<T>
public Iterator<T> iterator() {
return new Iterator<T>() {
int index = 0;
@Override
public boolean hasNext() {
return index < values.length;
}
@Override @SuppressWarnings("unchecked")
public T next() {
T current = (T) values[index];
++index;
return current;
}
};
}
Это не идеальное решение, это своего рода хак, но оно работает. Я думаю, что это разумное решение, и мне оно нравится. Спасибо.
Массивы и дженерики несовместимы. Просто используйте коллекцию, например список:
public List<Node> children = new ArrayList<Node>();
Разница в производительности будет незначительной.
Для поддержки устаревшего кода. Java предлагает «сырые» типы, чтобы обойти систему общих типов при работе с массивами. Ваша проблема в том, что Node является внутренним классом универсального класса и содержит неявные общие параметры. Тип действительно BTree<T, V>.Node. Вы не можете создать массив такого универсального типа. Чтобы создать массив, вам нужен «сырой» тип без каких-либо общих параметров. В вашем случае это тип BTree.Node:
class BTree<T> {
static final int MIN_DEGREE = 1;
private class Node {
public Node[] children = (Node[]) new BTree.Node[2 * MIN_DEGREE];
}
Node node = new Node();
public static void main(String[] args) {
new BTree();
}
}
Обратите внимание, что для этого требуется небезопасное приведение.
Спасибо за ваш ответ, но ArrayLists - это динамические массивы в java, и амортизированное постоянное время для вставки усложнило бы мне задачу. Мне нужна фактическая вставка и индекс постоянного времени.
And when I create
Keyarray without casting it givesGeneric array creationerror when compiling:public Key[] keys = new Key[2 * MIN_DEGREE - 1];
Причина этого в том, что Key является внутренним классом внутри универсального класса BTree<T, V>, поэтому, когда вы пишете Key отдельно внутри BTree, это неявно означает BTree<T, V>.Key, и это параметризованный тип, и вам не разрешено создавать массив параметризованный тип.
Когда люди хотят создать массив параметризованного типа, одним из решений является создание массива необработанного типа, а затем приведение его к типу «массив параметризованного типа». Сложный вопрос здесь заключается в том, как написать необработанный тип. Это не Key сам по себе, так как это параметризованный тип (поскольку он неявно определяется параметризованным внешним типом). Вместо этого вы должны явно квалифицировать его с помощью необработанного внешнего типа, чтобы записать необработанный тип внутреннего класса:
public Key[] keys = (Key[])new BTree.Key[2 * MIN_DEGREE - 1];
Вы пытаетесь преобразовать массив Comparables в массив Keys. Это похоже на преобразование множества млекопитающих (верблюдов, лошадей и кошек) в множество собак. Зачем вообще это делать? Создайте массив Key и массив Node.