Non è possibile creare un array di LinkedList in Java ...?


102

Sto lavorando su una classe di matrice sparsa che deve utilizzare un array di LinkedListper memorizzare i valori di una matrice. Ogni elemento della matrice (cioè ciascuno LinkedList) rappresenta una riga della matrice. Inoltre, ogni elemento nella LinkedListmatrice rappresenta una colonna e il valore memorizzato.

Nella mia classe, ho una dichiarazione dell'array come:

private LinkedList<IntegerNode>[] myMatrix;

E, nel mio costruttore per il SparseMatrix, cerco di definire:

myMatrix = new LinkedList<IntegerNode>[numRows];

L'errore che finisco per ottenere è

Impossibile creare un array generico di LinkedList<IntegerNode>.

Quindi, ho due problemi con questo:

  1. Cosa sto facendo di sbagliato, e
  2. Perché il tipo è accettabile nella dichiarazione per l'array se non può essere creato?

IntegerNodeè una classe che ho creato. E tutti i miei file di classe sono impacchettati insieme.

Risposte:


64

Non è possibile utilizzare la creazione di array generici. È un difetto / caratteristica dei generici java.

I modi senza avvisi sono:

  1. Utilizzo di List of Lists invece di Array of Lists:

    List< List<IntegerNode>> nodeLists = new LinkedList< List< IntegerNode >>();
  2. Dichiarazione della classe speciale per Array of Lists:

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

19
Una migliore alternativa a quest'ultima soluzione sarebbe: class IntegerNodeList extends List<IntegerNode> {}
kamasheto

Questa implementazione è scandalosamente lenta. Ottenere l'elemento [1000] [2000] (nodeLists.get (1000) .get (2000)) farà iterare LinkedList 3000 volte! Evita LinkedList se qualcuno vi sta indicizzando. ArrayList indicizzerà più velocemente, ma la soluzione di Fredrik è complessivamente migliore.
Steve Zobell

142

Per qualche motivo devi eseguire il cast del tipo e fare la dichiarazione in questo modo:

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

Ho ricercato un problema simile e ho letto che il cast di cui sopra è un "hack" molto comune che viene utilizzato in tutto il framework delle collezioni.
Luca

15
IMO, questa dovrebbe essere la risposta selezionata. Non ho sperimentato, ma ho la sensazione viscerale che il metodo n. 2 di Sergey crei un bel po 'di overhead; e sono POSITIVO che # 1 lo faccia. Un elenco non è efficiente come un array in diversi modi che non descriverò in dettaglio qui, ma ho fatto esperimenti e ho visto grossi rallentamenti nell'uso degli elenchi rispetto agli array. È più veloce gestire i propri array e riallocarli, piuttosto che aggiungere elementi a un elenco.
Ricket


4
Ricevo ancora un avviso "Protezione dal tipo: cast deselezionato". La soluzione di Bob mi sembra la più pulita.
Marco Lackovic

3
In JDK 7 quanto sopra fornisce un avviso di tipo raw. Ciò può essere risolto utilizzando il tipo <?> Illimitato, ma viene comunque visualizzato un avviso non controllato (che può essere soppresso). ad es. <br> <code> myMatrix = (LinkedList <IntegerNode> []) new LinkedList <?> [numRows]; </code>
Neon

5

A parte i problemi di sintassi, mi sembra strano usare un array e un elenco collegato per rappresentare una matrice. Per poter accedere a celle arbitrarie della matrice, probabilmente vorresti un array effettivo o almeno un ArrayListper contenere le righe, poiché LinkedListdevi attraversare l'intero elenco dal primo elemento a qualsiasi elemento particolare, O(n)un'operazione, al contrario di molto più veloce O(1)con ArrayListo un array effettivo.

Dato che hai menzionato che questa matrice è scarsa, forse un modo migliore per memorizzare i dati è come una mappa di mappe, dove una chiave nella prima mappa rappresenta un indice di riga e il suo valore è una mappa di riga le cui chiavi sono un indice di colonna , con il valore che è la tua classe IntegerNode. Così:

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

Se devi essere in grado di attraversare la matrice riga per riga, puoi fare in modo che la mappa di riga digiti a TreeMap, e lo stesso per attraversare le colonne in ordine di indice, ma se non hai bisogno di quei casi, HashMapè più veloce di TreeMap. I metodi di supporto per ottenere e impostare una cella arbitraria, gestendo valori nulli non impostati, sarebbero utili, ovviamente.


4
class IntegerNodeList extends LinkedList<IntegerNode> {}

IntegerNodeList[] myMatrix = new IntegerNodeList[numRows]; 

Ti sei perso i generici per LinkedList.
Peter Wippermann

3

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

lanciare in questo modo funziona ma ti lascia comunque con un brutto avvertimento:

"Sicurezza del tipo: l'espressione di tipo List [] richiede una conversione non selezionata .."

Dichiarazione di una classe speciale per array di elenchi:

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

è un'idea intelligente per evitare l'avviso. forse un po 'più bello è usare un'interfaccia per questo:

public interface IntegerNodeList extends List<IntegerNode> {}

poi

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

si compila senza avvisi.

non sembra tanto male, vero?


IntegerNodeList: con quale classe useresti questo? Ad esempio, non è possibile assegnargli un ArrayList <IntegerNode>. Dovresti estendere anche ArrayList ...
Hans-Peter Störr

non è necessario utilizzare l'interfaccia IntegerNodeList al di fuori dell'inizializzazione dell'array: 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];Questo ha un problema sottile ma importante. Puoi solo inserire IntegerNodeListl'array. myMatrix[i] = new ArrayList<IntegerNode>();lancerà ArrayStoreException.
Radiodef

2
List<String>[] lst = new List[2];
lst[0] = new LinkedList<String>();
lst[1] = new LinkedList<String>();

Nessun avvertimento. NetBeans 6.9.1, jdk1.6.0_24


1
Vero nessun avviso ma con l'aggiornamento 32 di Java SE 6 di Oracle ottengo l'errore di compilazione "Il tipo List non è generico; non può essere parametrizzato con argomenti <String>". La rimozione dell'argomento <String> genera un altro errore "Tipo non corrispondente: impossibile eseguire la conversione da LinkedList <String> a List".
Marco Lackovic


0

Se eseguo le operazioni seguenti, viene visualizzato il messaggio di errore in questione

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

Ma se rimuovo solo il tipo di elenco nella dichiarazione, sembra che abbia la funzionalità desiderata.

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

Queste due dichiarazioni sono drasticamente diverse in un modo di cui non sono a conoscenza?

MODIFICARE

Ah, penso di essermi imbattuto in questo problema ora.

L'iterazione sulla matrice e l'inizializzazione degli elenchi in un ciclo for sembrano funzionare. Anche se non è l'ideale come alcune delle altre soluzioni offerte.

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

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

0

Hai bisogno di un array di List, un'alternativa è provare:

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

Quindi node_array[i]memorizza il (primo) nodo head di un ArrayList<IntegerNode>o LinkedList<IntegerNode>(qualunque sia l'implementazione dell'elenco dei preferiti).

Con questo design, perdi il metodo di accesso casuale list.get(index), ma puoi comunque attraversare l'elenco iniziando con l'archivio del nodo head / fist nell'array type safe.

Questa potrebbe essere una scelta progettuale accettabile a seconda del tuo caso d'uso. Ad esempio, utilizzo questo design per rappresentare un elenco di grafo di adiacenza, nella maggior parte dei casi d'uso richiede comunque di attraversare l'elenco di adiacenza per un dato vertice invece di accedere casualmente a qualche vertice nell'elenco.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.