Perché un array non è assegnabile a Iterable?


186

con Java5 possiamo scrivere:

Foo[] foos = ...
for (Foo foo : foos) 

o semplicemente usando un Iterable nel ciclo for. Questo è molto utile.

Tuttavia non è possibile scrivere un metodo generico per iterable in questo modo:

public void bar(Iterable<Foo> foos) { .. }

e chiamandolo con un array poiché non è Iterable:

Foo[] foos = { .. };
bar(foos);  // compile time error 

Mi chiedo i motivi alla base di questa decisione progettuale.


8
Arrays.asList è abbastanza buono suppongo
dfa

17
è una domanda filosofica
dfa

2
un buon motivo per gestire le matrici in Java 5+ sono i metodi varargs.
Jeff Walker,

2
@Torsten: vero, ma se lo passi a un metodo che accetta un Iterable, probabilmente non stai apportando alcuna modifica.
Michael Myers

5
In realtà, Arrays.asList non è abbastanza buono perché non funziona su array di tipi primitivi. L'unico modo integrato per iterare genericamente (boxed) elementi di tipi primitivi è con la riflessione, usando java.lang.reflect.Array, ma le sue prestazioni sono deboli. Tuttavia, puoi scrivere i tuoi iteratori (o le implementazioni di Elenco!) Per avvolgere array di tipi primitivi, se lo desideri.
Boann,

Risposte:


78

Le matrici possono implementare interfacce ( Cloneablee java.io.Serializable). Allora perché no Iterable? Immagino che le Iterableforze aggiungano un iteratormetodo e le matrici non implementano metodi. char[]non ha nemmeno la precedenza toString. In ogni caso, le matrici di riferimenti dovrebbero essere considerate non ideali List. Come commenta il dfa, Arrays.asListfarà la conversione per te, esplicitamente.

(Detto questo, puoi chiamare clonearray.)


23
> "... e le matrici non implementano metodi." Penso che questa sia un'altra domanda filosofica; le matrici non erano mai tipi primitivi e la filosofia Java dice che "Tutto è un oggetto (tranne i tipi primitivi)". Perché, quindi, le matrici non implementano metodi anche se ci sono operazioni gazillion per le quali si vorrebbe usare un array dall'inizio. Oh, è vero, le matrici erano le uniche raccolte fortemente tipizzate prima che i generici arrivassero come un triste senno di poi.
Fatuhoku,

2
Se disponi di dati in un array, è possibile che tu stia eseguendo un lavoro di basso livello, critico per le prestazioni, come gestire i byte di lettura [] dai flussi. L'incapacità di iterare le matrici deriva probabilmente dai generici Java che non supportano le primitive come argomenti di tipo, come dice @Gareth di seguito.
Drew Noakes, il

2
@FatuHoku Suggerire che i generici fossero un dispiaciuto senno di poi non è corretto. L'opportunità dei generici è stata sempre apprezzata. Le matrici non sono primitive (non ho detto che lo fossero), ma sono di basso livello. L'unica cosa che vuoi fare con le matrici è usarle come dettaglio di implementazione per strutture simili a vettori.
Tom Hawtin - tackline

1
Iterator<T>richiede anche remove(T), anche se è consentito lanciare un UnsupportedOperationException.
wchargin,

Gli array implementano metodi: implementano tutti i metodi di java.lang.Object.
mhsmith,

59

L'array è un oggetto, ma i suoi elementi potrebbero non esserlo. L'array potrebbe contenere un tipo primitivo come int, che Iterable non può far fronte. Almeno questo è quello che credo.


3
Ciò significa che per supportare l' Iterableinterfaccia, le matrici primitive devono essere specializzate per utilizzare le classi wrapper. Niente di tutto questo è davvero un grosso problema, poiché i parametri di tipo sono comunque tutti falsi.
thejoshwolfe,

8
Ciò non impedirebbe alle matrici di oggetti di implementare Iterable. Inoltre, non impedirebbe alle matrici primitive di implementare Iterable per il tipo di wrapping.
Boann,

1
Autoboxing potrebbe gestirlo
Tim Büthe

Penso che questa sia la ragione corretta. Non funzionerà in modo soddisfacente per le matrici di tipi primitivi, fino a quando Generics non supporterà tipi primitivi (ad es. List<int>Invece di List<Integer>, ecc.). Un hack potrebbe essere eseguito con wrapper ma con una perdita di prestazioni - e soprattutto - se questo hack fosse fatto, in futuro impedirebbe di implementarlo correttamente in Java (ad esempio int[].iterator()sarebbe bloccato per sempre per restituire Iterator<Integer>piuttosto che per Iterator<int>). Forse, l'imminente valore-tipi + specializzazione generica per Java (progetto valhalla) farà implementare le matrici Iterable.
Bjarke,

16

Le matrici dovrebbero supportare Iterable, semplicemente non lo sono, per lo stesso motivo per cui le matrici .NET non supportano un'interfaccia che consente un accesso casuale di sola lettura per posizione (non esiste una tale interfaccia definita come standard). Fondamentalmente, i framework hanno spesso fastidiosi buchi in loro, che non vale la pena a nessuno di riparare. Non importa se potremmo risolverli da soli in modo ottimale, ma spesso non possiamo.

AGGIORNAMENTO: Per essere omogeneo, ho menzionato array .NET che non supportano un'interfaccia che supporta l'accesso casuale per posizione (vedere anche il mio commento). Ma in .NET 4.5 quell'interfaccia esatta è stata definita ed è supportata da array e dalla List<T>classe:

IReadOnlyList<int> a = new[] {1, 2, 3, 4};
IReadOnlyList<int> b = new List<int> { 1, 2, 3, 4 };

Non tutto è ancora del tutto perfetto perché l'interfaccia dell'elenco mutabile IList<T>non eredita IReadOnlyList<T>:

IList<int> c = new List<int> { 1, 2, 3, 4 };
IReadOnlyList<int> d = c; // error

Forse c'è una possibile compatibilità con le versioni precedenti con un tale cambiamento.

Se ci sono progressi su cose simili nelle nuove versioni di Java, sarei interessato a sapere nei commenti! :)


8
Le matrici .NET implementano l'interfaccia IList
Tom Gillen,

2
@Aphid - Ho detto un accesso casuale di sola lettura . IList<T>espone le operazioni per la modifica. Sarebbe bello se IList<T>aveva qualcosa ereditata come IReadonlyList<T>un'interfaccia, che avrebbe appena avuto Counte T this[int]ed ereditata IEnumerable<T>(che supporta già l'enumerazione di sola lettura). Un'altra cosa interessante sarebbe un'interfaccia per ottenere un enumeratore di ordine inverso, per il quale il Reversemetodo di estensione potrebbe richiedere (proprio come il Countmetodo di estensione richiede ICollectiondi ottimizzare se stesso).
Daniel Earwicker

Sì, sarebbe molto meglio se le cose fossero state progettate in quel modo. L'interfaccia IList definisce le proprietà IsReadOnly e IsFixedSize, che sono implementate in modo appropriato dall'array. Questo mi ha sempre sembrato un modo molto brutto di farlo, poiché non offre alcun tempo di compilazione per verificare che l'elenco che ti viene dato sia in effetti di sola lettura e molto raramente vedo codice che controlla queste proprietà.
Tom Gillen,

1
Le matrici in .NET implementano IListe ICollectionda .NET 1.1 IList<T>e ICollection<T>da .NET 2.0. Questo è un altro caso in cui Java è molto indietro rispetto alla concorrenza.
Amir Abiri,

@ TomGillen: il mio problema maggiore IListè che non fornisce attributi più interrogabili. Direi che un set adeguato dovrebbe includere IsUpdateable, IsResizable, IsReadOnly, IsFixedSize e ExistingElementsAreImmutable per i principianti. La domanda se un codice può fare riferimento a un riferimento, senza la tipografia, è separata dalla domanda se il codice che contiene un riferimento a un elenco che non dovrebbe modificare possa condividere in modo sicuro quel riferimento direttamente con un codice esterno o se può tranquillamente presumere che alcuni aspetti dell'elenco non cambieranno mai.
supercat

14

Sfortunatamente, le matrici non sono " classabbastanza". Non implementano l' Iterableinterfaccia.

Mentre gli array sono ora oggetti che implementano Clonable e Serializable, credo che un array non sia un oggetto in senso normale e non implementi l'interfaccia.

Il motivo per cui puoi usarli in ogni ciclo è perché Sun ha aggiunto dello zucchero sintetico per gli array (è un caso speciale).

Da quando gli array sono iniziati come "quasi oggetti" con Java 1, sarebbe troppo drastico un cambiamento per renderli oggetti reali in Java.


14
Tuttavia, c'è zucchero per ogni ciclo, quindi perché non può esserci zucchero per Iterable?
Michael Myers

8
@mmyers: lo zucchero usato in per ciascuno è lo zucchero in fase di compilazione . È molto più facile da fare rispetto allo zucchero VM . Detto questo, gli array .NET sono significativamente migliori in quest'area ...
Jon Skeet,

12
Le matrici possono implementare interfacce. Implementano Cloneablee si Serializableinterfacciano.
notnoop,

34
le matrici Java sono oggetti in tutti i sensi. Rimuovi quel poco di disinformazione. Semplicemente non implementano Iterable.
ykaganovich,

5
Un array è un oggetto. Supporta utili : metodi P come wait (), wait (n), wait (n, m), notify (), notifyAll (), finalize (), un'implementazione inutile di toString () L'unico metodo utile è getClass () .
Peter Lawrey,

1

Il compilatore traduce in realtà for eachun array su un semplice forciclo con una variabile contatore.

Compilando quanto segue

public void doArrayForEach() {
    int[] ints = new int[5];

    for(int i : ints) {
        System.out.println(i);
    }
}

e quindi decompilare i rendimenti del file .class

public void doArrayForEach() {
    int[] ints = new int[5];
    int[] var2 = ints;
    int var3 = ints.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        int i = var2[var4];
        System.out.println(i);
    }
}
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.