Не удается создать массив LinkedLists в Java…?

102

Я работаю над классом разреженных матриц, который должен использовать массив LinkedListдля хранения значений матрицы. Каждый элемент массива (т.е. каждый LinkedList) представляет собой строку матрицы. И каждый элемент в LinkedListмассиве представляет столбец и сохраненное значение.

В моем классе у меня есть объявление массива как:

private LinkedList<IntegerNode>[] myMatrix;

И в своем конструкторе SparseMatrixя пытаюсь определить:

myMatrix = new LinkedList<IntegerNode>[numRows];

Ошибка, которую я получаю, это

Невозможно создать общий массив из LinkedList<IntegerNode>.

Итак, у меня есть две проблемы:

  1. Что я делаю не так и
  2. Почему тип приемлем в объявлении для массива, если он не может быть создан?

IntegerNodeэто класс, который я создал. И все мои файлы классов упакованы вместе.

кафучау
источник

Ответы:

64

Вы не можете использовать создание универсального массива. Это недостаток / особенность дженериков Java.

Способы без предупреждений:

  1. Использование списка списков вместо массива списков:

    List< List<IntegerNode>> nodeLists = new LinkedList< List< IntegerNode >>();
  2. Объявление специального класса для массива списков:

    class IntegerNodeList {
        private final List< IntegerNode > nodes;
    }
    
Сергей
источник
19
class IntegerNodeList extends List<IntegerNode> {}
Лучшей
Эта реализация возмутительно медленная. При получении элемента [1000] [2000] (nodeLists.get (1000) .get (2000)) LinkedList будет повторяться 3000 раз! Избегайте LinkedList, если кто-то может индексировать его. ArrayList будет индексировать быстрее, но решение Фредрика в целом лучше.
Стив Зобелл, 09
142

По какой-то причине вам нужно привести тип и сделать объявление следующим образом:

myMatrix = (LinkedList<IntegerNode>[]) new LinkedList<?>[numRows];
Фредрик
источник
Я исследовал аналогичную проблему и прочитал, что приведенное выше приведение - очень распространенный «взлом», который используется во всей структуре коллекций.
люк
15
ИМО, это должен быть выбранный ответ. Я не экспериментировал, но мне кажется, что метод №2 Сергея создает довольно много накладных расходов; и я ПОЗИТИВНО, что № 1 делает. Список не так эффективен, как массив, по нескольким причинам, которые я не буду здесь подробно описывать, но я проводил эксперименты и видел большие замедления при использовании списков по сравнению с массивами. Быстрее просто управлять своими собственными массивами и перераспределять их, чем добавлять что-то в список.
Ricket
@Ricket Я согласен, взятый из ibm.com/developerworks/java/library/j-jtp01255/index.html
Peteter
4
Я по-прежнему получаю предупреждение "Типовая безопасность: непроверенное приведение". Решение Боба мне кажется самым чистым.
Марко Лацкович
3
В JDK 7 приведенное выше дает предупреждение rawtypes. Это можно исправить с помощью неограниченного типа <?>, Но вы все равно получите непроверенное предупреждение (которое можно подавить). например, <br> <code> myMatrix = (LinkedList <IntegerNode> []) новый LinkedList <?> [numRows]; </code>
Neon,
5

Помимо проблем с синтаксисом, мне кажется странным использовать массив и связанный список для представления матрицы. Чтобы иметь возможность получить доступ к произвольным ячейкам матрицы, вам, вероятно, понадобится фактический массив или, по крайней мере, ArrayListдля хранения строк, так как он LinkedListдолжен проходить весь список от первого элемента до любого конкретного элемента, O(n)операция, в отличие от многих быстрее O(1)с ArrayListили фактическим массивом.

Поскольку вы упомянули, что эта матрица является разреженной, возможно, лучший способ хранить данные - это карта карт, где ключ в первой карте представляет индекс строки, а его значение - карта строки, ключи которой являются индексом столбца , со значением вашего класса IntegerNode. Таким образом:

private Map<Integer, Map<Integer, IntegerNode>> myMatrix = new HashMap<Integer, Map<Integer, IntegerNode>>();

// access a matrix cell:
int rowIdx = 100;
int colIdx = 30;
Map<Integer, IntegerNode> row = myMatrix.get(rowIdx); // if null, create and add to matrix
IntegerNode node = row.get(colIdx); // possibly null

Если вам нужно иметь возможность перемещаться по матрице строка за строкой, вы можете сделать карту строк типом a TreeMap, и то же самое для обхода столбцов в порядке индекса, но если вам не нужны эти случаи, HashMapэто быстрее, чем TreeMap. Конечно, были бы полезны вспомогательные методы для получения и установки произвольной ячейки, обрабатывающие неустановленные нулевые значения.

Дов Вассерман
источник
4
class IntegerNodeList extends LinkedList<IntegerNode> {}

IntegerNodeList[] myMatrix = new IntegerNodeList[numRows]; 
Боб
источник
Вы пропустили дженерики для LinkedList.
Питер Випперманн
3

myMatrix = (LinkedList<IntegerNode>[]) new LinkedList[numRows];

приведение такого способа работает, но по-прежнему оставляет вам неприятное предупреждение:

«Безопасность типов: выражение типа List [] требует неконтролируемого преобразования ..»

Объявление специального класса для массива списков:

class IntegerNodeList { private final List< IntegerNode > nodes; }

это умная идея, чтобы избежать предупреждения. может быть, немного лучше использовать для этого интерфейс:

public interface IntegerNodeList extends List<IntegerNode> {}

затем

List<IntegerNode>[] myMatrix = new IntegerNodeList[numRows];

компилируется без предупреждений.

не так уж плохо выглядит, правда?

user306708
источник
IntegerNodeList: с каким классом вы бы это использовали? Например, вы не могли назначить ему ArrayList <IntegerNode>. Вам также нужно будет расширить ArrayList ...
Ханс-Петер Штёрр, 01
нет необходимости использовать интерфейс IntegerNodeList вне инициализации массива: List <IntegerNode> [] myMatrix = new IntegerNodeList [5]; для (int i = 0; i <myMatrix.length; i ++) {myMatrix [i] = new ArrayList <IntegerNode> (); }
user306708 01
1
List<IntegerNode>[] myMatrix = new IntegerNodeList[numRows];Это тонкая, но важная проблема. Можно только положить IntegerNodeListв массив. myMatrix[i] = new ArrayList<IntegerNode>();кинет ArrayStoreException.
Radiodef 08
2
List<String>[] lst = new List[2];
lst[0] = new LinkedList<String>();
lst[1] = new LinkedList<String>();

Никаких предупреждений. NetBeans 6.9.1, jdk1.6.0_24

Андрей
источник
1
True без предупреждений, но с Oracle Java SE 6 Update 32 я получаю сообщение об ошибке компиляции «Список типов не является универсальным; его нельзя параметризовать аргументами <String>». Удаление аргумента <String> приводит к возникновению другой ошибки «Несоответствие типа: невозможно преобразовать из LinkedList <String> в список».
Марко Лацкович
0

Если я сделаю следующее, я получу соответствующее сообщение об ошибке

LinkedList<Node>[] matrix = new LinkedList<Node>[5];

Но если я просто удалю тип списка в объявлении, он, похоже, имеет желаемую функциональность.

LinkedList<Node>[] matrix = new LinkedList[5];

Эти два заявления сильно отличаются друг от друга, о чем я не знаю?

РЕДАКТИРОВАТЬ

А, думаю, я столкнулся с этой проблемой сейчас.

Кажется, что перебор матрицы и инициализация списков в цикле for работают. Хотя это не так идеально, как некоторые другие предлагаемые решения.

for(int i=0; i < matrix.length; i++){

    matrix[i] = new LinkedList<>();
}
Райан
источник
0

Вам нужен массив List, одна из альтернатив - попробовать:

private IntegerNode[] node_array = new IntegerNode[sizeOfYourChoice];

Затем node_array[i]сохраняет головной (первый) узел ArrayList<IntegerNode>или LinkedList<IntegerNode>(независимо от вашей любимой реализации списка).

При такой схеме вы теряете метод произвольного доступа list.get(index), но тогда вы все равно можете перемещаться по списку, начиная с хранилища узлов head / fist в массиве типов.

Это может быть приемлемым выбором дизайна в зависимости от вашего варианта использования. Например, я использую этот дизайн для представления списка смежности графа, в большинстве случаев это требует обхода списка смежности для данной вершины вместо произвольного доступа к какой-либо вершине в списке.

Yiling
источник