Nie można utworzyć tablicy LinkedLists w Javie…?

102

Pracuję nad rzadką klasą macierzy, która musi używać tablicy LinkedListdo przechowywania wartości macierzy. Każdy element tablicy (tj. Każdy LinkedList) reprezentuje wiersz macierzy. Każdy element LinkedListtablicy reprezentuje kolumnę i przechowywaną wartość.

W mojej klasie mam deklarację tablicy jako:

private LinkedList<IntegerNode>[] myMatrix;

W moim konstruktorze dla programu SparseMatrixpróbuję zdefiniować:

myMatrix = new LinkedList<IntegerNode>[numRows];

Błąd, który otrzymuję, to

Nie można utworzyć ogólnej tablicy LinkedList<IntegerNode>.

Mam z tym dwa problemy:

  1. Co robię źle i
  2. Dlaczego typ jest dopuszczalny w deklaracji tablicy, jeśli nie można jej utworzyć?

IntegerNodeto klasa, którą stworzyłem. Wszystkie pliki moich zajęć są spakowane razem.

kafuchau
źródło

Odpowiedzi:

64

Nie możesz użyć ogólnego tworzenia tablicy. Jest to wada / funkcja generycznych języków Java.

Sposoby bez ostrzeżeń to:

  1. Korzystanie z listy list zamiast tablicy list:

    List< List<IntegerNode>> nodeLists = new LinkedList< List< IntegerNode >>();
  2. Deklarowanie specjalnej klasy dla tablicy list:

    class IntegerNodeList {
        private final List< IntegerNode > nodes;
    }
    
Siergiej
źródło
19
Lepszą alternatywą dla tego drugiego rozwiązania byłoby: class IntegerNodeList extends List<IntegerNode> {}
kamasheto
Ta implementacja jest skandalicznie powolna. Pobranie elementu [1000] [2000] (nodeLists.get (1000) .get (2000)) spowoduje, że LinkedList będzie iterował 3000 razy! Unikaj LinkedList, jeśli ktoś może go indeksować. ArrayList indeksuje szybciej, ale rozwiązanie Fredrika jest ogólnie lepsze.
Steve Zobell
142

Z jakiegoś powodu musisz rzutować typ i zrobić taką deklarację:

myMatrix = (LinkedList<IntegerNode>[]) new LinkedList<?>[numRows];
Fredrik
źródło
Zbadałem podobny problem i przeczytałem, że powyższa obsada to bardzo powszechny „hack”, który jest używany we wszystkich ramach kolekcji.
Łukasz
15
IMO, to powinna być wybrana odpowiedź. Nie eksperymentowałem, ale mam przeczucie, że metoda nr 2 Siergieja stwarza spore obciążenie; i jestem POZYTYWNY, że # 1 tak. Lista nie jest tak wydajna jak tablica pod kilkoma względami, których nie będę tutaj szczegółowo opisywać, ale ZROBIŁEM eksperymenty i zauważyłem duże spowolnienia podczas korzystania z list w porównaniu z tablicami. Szybciej jest po prostu zarządzać własnymi tablicami i ponownie je przydzielać, niż dodawać rzeczy do listy.
Ricket
@Ricket, zgadzam się, zaczerpnięte z ibm.com/developerworks/java/library/j-jtp01255/index.html
Peteter
4
Nadal otrzymuję ostrzeżenie „Bezpieczeństwo typów: niezaznaczona obsada”. Rozwiązanie Boba wydaje mi się najczystsze.
Marco Lackovic
3
W JDK 7 powyższe daje ostrzeżenie o surowych typach. Można to naprawić za pomocą typu nieograniczonego <?>, Ale nadal pojawia się niesprawdzone ostrzeżenie (które można pominąć). np. <br> <code> myMatrix = (LinkedList <IntegerNode> []) new LinkedList <?> [numRows]; </code>
Neon
5

Pomijając problemy ze składnią, wydaje mi się dziwne używanie tablicy i połączonej listy do reprezentowania macierzy. Aby móc uzyskać dostęp do dowolnych komórek macierzy, prawdopodobnie chciałbyś, aby rzeczywista tablica lub przynajmniej ArrayListprzechowywała wiersze, ponieważ LinkedListmusi przejść całą listę od pierwszego elementu do dowolnego konkretnego elementu, O(n)operacji, w przeciwieństwie do dużo szybciej O(1)z ArrayListlub rzeczywistą tablicą.

Ponieważ wspomniałeś, że ta macierz jest rzadka, być może lepszym sposobem przechowywania danych jest mapa map, gdzie klucz w pierwszej mapie reprezentuje indeks wierszy, a jego wartością jest mapa wierszy, której klucze są indeksem kolumny , z wartością będącą Twoją klasą IntegerNode. A zatem:

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

Jeśli potrzebujesz mieć możliwość przechodzenia przez macierz wiersz po wierszu, możesz sprawić, że mapa wierszy będzie miała typ a TreeMapi to samo dla przechodzenia przez kolumny w kolejności indeksu, ale jeśli nie potrzebujesz tych przypadków, HashMapjest to szybsze niż TreeMap. Oczywiście przydatne byłyby metody pomocnicze do pobierania i ustawiania dowolnej komórki, obsługujące nieustawione wartości null.

Dov Wasserman
źródło
4
class IntegerNodeList extends LinkedList<IntegerNode> {}

IntegerNodeList[] myMatrix = new IntegerNodeList[numRows]; 
Pion
źródło
Przegapiłeś generyczne dla LinkedList.
Peter Wippermann
3

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

przesyłanie w ten sposób działa, ale nadal pozostawia nieprzyjemne ostrzeżenie:

„Bezpieczeństwo typów: wyrażenie typu List [] wymaga niesprawdzonej konwersji.”

Deklarowanie specjalnej klasy dla tablicy list:

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

to sprytny pomysł, aby uniknąć ostrzeżenia. może trochę przyjemniej jest użyć do tego interfejsu:

public interface IntegerNodeList extends List<IntegerNode> {}

następnie

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

kompiluje się bez ostrzeżeń.

nie wygląda tak źle, prawda?

user306708
źródło
IntegerNodeList: z jaką klasą byś tego użył? Na przykład nie można było przypisać do niego ArrayList <IntegerNode>. Musiałbyś również rozszerzyć ArrayList ...
Hans-Peter Störr
nie ma potrzeby używania interfejsu IntegerNodeList poza inicjalizacją tablicy: List <IntegerNode> [] myMatrix = new IntegerNodeList [5]; for (int i = 0; i <myMatrix.length; i ++) {myMatrix [i] = new ArrayList <IntegerNode> (); }
user306708
1
List<IntegerNode>[] myMatrix = new IntegerNodeList[numRows];Jest to subtelny, ale ważny problem. Możesz tylko wstawić IntegerNodeListtablicę. myMatrix[i] = new ArrayList<IntegerNode>();rzuci ArrayStoreException.
Radiodef
2
List<String>[] lst = new List[2];
lst[0] = new LinkedList<String>();
lst[1] = new LinkedList<String>();

Żadnych ostrzeżeń. NetBeans 6.9.1, jdk1.6.0_24

Andrii
źródło
1
Prawda, brak ostrzeżeń, ale w przypadku Oracle Java SE 6 Update 32 pojawia się błąd kompilacji „Lista typów nie jest ogólna; nie można jej sparametryzować za pomocą argumentów <String>”. Usunięcie argumentu <String> generuje kolejny błąd „Niezgodność typu: nie można przekonwertować z LinkedList <String> na List”.
Marco Lackovic
0

Jeśli wykonam następujące czynności, pojawi się odpowiedni komunikat o błędzie

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

Ale jeśli po prostu usunę typ listy w deklaracji, wydaje się, że ma ona pożądaną funkcjonalność.

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

Czy te dwie deklaracje różnią się drastycznie w sposób, którego nie jestem świadomy?

EDYTOWAĆ

Ach, myślę, że mam teraz ten problem.

Wydaje się, że Iterowanie po macierzy i inicjowanie list w pętli for działa. Chociaż nie jest tak idealny, jak niektóre inne oferowane rozwiązania.

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

    matrix[i] = new LinkedList<>();
}
Ryan
źródło
0

Potrzebujesz tablicy List, jedną z alternatyw jest wypróbowanie:

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

Następnie node_array[i]przechowuje węzeł główny (pierwszy) ArrayList<IntegerNode>lub LinkedList<IntegerNode>(niezależnie od implementacji listy ulubionych).

W tym projekcie tracisz metodę dostępu swobodnego list.get(index), ale nadal możesz przeglądać listę, zaczynając od magazynu węzłów head / fist w tablicy typu safe.

Może to być akceptowalny wybór projektu w zależności od przypadku użycia. Na przykład, używam tego projektu do reprezentowania listy sąsiedztwa grafu, w większości przypadków użycia wymaga on i tak przejścia przez listę sąsiedztwa dla danego wierzchołka zamiast swobodnego dostępu do jakiegoś wierzchołka na liście.

Yiling
źródło