Не вдається створити масив LinkedLists на Java…?


102

Я працюю над класом розрідженої матриці, який повинен використовувати масив, LinkedListщоб зберігати значення матриці. Кожен елемент масиву (тобто кожен LinkedList) являє собою рядок матриці. І кожен елемент LinkedListмасиву представляє стовпець і збережене значення.

У своєму класі я маю декларацію масиву як:

private LinkedList<IntegerNode>[] myMatrix;

І в своєму конструкторі для цього SparseMatrixя намагаюся визначити:

myMatrix = new LinkedList<IntegerNode>[numRows];

Отримана помилка - це

Неможливо створити загальний масив LinkedList<IntegerNode>.

Отже, у мене є два питання з цим:

  1. Що я роблю неправильно, і
  2. Чому тип є прийнятним в декларації для масиву, якщо його неможливо створити?

IntegerNodeце клас, який я створив. І всі файли мого класу пакуються разом.

Відповіді:


64

Ви не можете використовувати загальне створення масиву. Це вада / особливість java generics.

Способи без попереджень:

  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 індексує швидше, але рішення Фредріка в цілому краще.
Стів Зобель

142

З якоїсь причини вам потрібно ввести тип і зробити декларацію так:

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

Я дослідив подібну проблему і прочитав, що вищезазначений склад є дуже поширеним "хаком", який використовується у всіх системах колекцій.
luke

15
ІМО, це має бути обрана відповідь. Я не експериментував, але відчуваю, що метод №2 Сергія створює досить великі витрати; і я ПОЗИТИВНО, що це робить # 1. Список не є настільки ефективним, як масив кількома способами, про які я тут не буду деталізувати, але я ВПРАВУвав експерименти і побачив великі уповільнення при використанні списків порівняно з масивами. Швидше просто керувати своїми масивами та перерозподіляти їх, ніж додавати матеріали до Списку.
Ricket

@Ricket Я згоден, взято з ibm.com/developerworks/java/library/j-jtp01255/index.html
Peteter

4
Я все ще отримую попередження "Тип безпеки: невірно відзначений". Рішення Боба для мене виглядає найчистішим.
Марко Лацкович

3
У JDK 7 вищезазначене дає попередження про вихідні типи. Це можна виправити за допомогою безмежного типу <?>, Але ви все одно отримаєте неперевірене попередження (яке можна придушити). Наприклад, <code> myMatrix = (LinkedList <IntegerNode> []) новий LinkedList <?> [numRows]; </code>
Неон

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. Зрозуміло, корисними будуть методи отримання та встановлення довільної комірки, обробка невстановлених нульових значень.



3

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

Цей спосіб працює, але все одно залишає вас з неприємним попередженням:

"Безпека типу: Вираз List List [] потребує неперевіреної конверсії .."

Оголошення спеціального класу для масиву списків:

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

- розумна ідея уникати попередження. можливо, трохи приємніше використовувати інтерфейс для нього:

public interface IntegerNodeList extends List<IntegerNode> {}

тоді

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

компілює без попереджень.

не дуже погано виглядає, чи не так?


IntegerNodeList: для якого класу ви б це використовували? Наприклад, ви не можете призначити ArrayList <IntegerNode> для нього. Вам також потрібно буде розширити ArrayList ...
Hans-Peter Störr

не потрібно використовувати інтерфейс IntegerNodeList поза ініціалізацією масиву: Список <IntegerNode> [] myMatrix = новий IntegerNodeList [5]; for (int i = 0; i <myMatrix.length; i ++) {myMatrix [i] = новий ArrayList <IntegerNode> (); }
користувач306708

1
List<IntegerNode>[] myMatrix = new IntegerNodeList[numRows];У цьому є тонка, але важлива проблема. Ви можете лише помістити IntegerNodeListв масив. myMatrix[i] = new ArrayList<IntegerNode>();кине ArrayStoreException.
Radiodef

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
Правда, жодних застережень, але з оновленням Java SE 6 Oracle 32 я отримую помилку компіляції "Список типів не є загальним; його неможливо параметризувати аргументами <String>". Видалення аргументу <String> створює ще одну помилку "Введіть невідповідність: неможливо перетворити з LinkedList <String> у Список".
Марко Лацкович


0

Якщо я виконаю наступне, я отримаю повідомлення про помилку

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

Але якщо я просто видалю тип списку в декларації, схоже, він має бажану функціональність.

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

Чи різняться ці дві декларації кардинально різними способами, про які я не знаю?

EDIT

Ах, я думаю, я зараз зіткнувся з цим питанням.

Ітерація над матрицею та ініціалізація списків у циклі for, здається, працюють. Хоча це не так ідеально, як деякі інші запропоновані рішення.

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

    matrix[i] = new LinkedList<>();
}

0

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

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

Потім node_array[i]зберігається головний (перший) вузол ArrayList<IntegerNode>або LinkedList<IntegerNode>(незалежно від вашої реалізації списку улюблених).

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

Це може бути прийнятним вибором дизайну залежно від Вашого випадку використання. Наприклад, я використовую цю конструкцію для представлення списку суміжності графіка, в більшості випадків використання вимагає переходу списку суміжності для даної вершини замість випадкового доступу до якоїсь вершини у списку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.