Pourquoi un tableau n'est-il pas attribuable à Iterable?

186

avec Java5, nous pouvons écrire:

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

ou simplement en utilisant un Iterable dans la boucle for. Ceci est très pratique.

Cependant, vous ne pouvez pas écrire une méthode générique pour itérable comme ceci:

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

et l'appelant avec un tableau car ce n'est pas un Iterable:

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

Je me demande les raisons de cette décision de conception.

dfa
la source
8
Arrays.asList est assez bon je suppose
dfa
17
c'est une question philosophique
dfa
2
les méthodes varargs constituent une bonne raison de traiter les tableaux dans Java 5+.
Jeff Walker
2
@Torsten: vrai, mais si vous le transmettez à une méthode qui accepte un Iterable, vous n'effectuez probablement aucune modification de toute façon.
Michael Myers
5
En fait, Arrays.asList n'est pas assez bon car il ne fonctionne pas sur des tableaux de types primitifs. Le seul moyen intégré d'itérer génériquement des éléments (encadrés) de types primitifs consiste à utiliser la réflexion java.lang.reflect.Array, mais ses performances sont faibles. Cependant, vous pouvez écrire vos propres itérateurs (ou implémentations List!) Pour encapsuler des tableaux de types primitifs si vous le souhaitez.
Boann

Réponses:

78

Les tableaux peuvent implémenter des interfaces ( Cloneableet java.io.Serializable). Alors pourquoi pas Iterable? Je suppose que les Iterableforces ajoutent une iteratorméthode et que les tableaux n'implémentent pas de méthodes. char[]ne l'emporte même pas toString. Quoi qu'il en soit, les tableaux de références devraient être considérés comme moins qu'idéaux - utilisez le Lists. Comme le commente dfa, Arrays.asListfera la conversion pour vous, explicitement.

(Cela dit, vous pouvez faire appel cloneà des tableaux.)

Tom Hawtin - Tacle
la source
23
> "... et les tableaux n'implémentent pas de méthodes." Je pense que c'est une autre question philosophique; les tableaux n'ont jamais été des types primitifs, et la philosophie Java dit que "tout est un objet (sauf les types primitifs)". Pourquoi, alors, les tableaux n'implémentent-ils pas de méthodes même s'il y a des milliards d'opérations pour lesquelles on voudrait utiliser un tableau depuis le début. Oh, c'est vrai, les tableaux étaient les seules collections fortement typées avant que les génériques n'apparaissent comme un triste recul.
fatuhoku
2
Si vous avez des données dans un tableau, il est possible que vous fassiez un travail critique de bas niveau en termes de performances, comme le traitement de la lecture d'octet [] à partir de flux. L'incapacité d'itérer les tableaux provient probablement des génériques Java ne prenant pas en charge les primitives comme arguments de type, comme @Gareth le dit ci-dessous.
Drew Noakes
2
@FatuHoku Suggérer que les génériques étaient un désolé recul est incorrect. L'opportunité des génériques a toujours été appréciée. Les tableaux ne sont pas des primitifs (je n'ai pas dit qu'ils l'étaient), mais ils sont de bas niveau. La seule chose que vous voulez faire avec les tableaux est de les utiliser comme détail d'implémentation pour les structures vectorielles.
Tom Hawtin - tackline
1
Iterator<T>nécessite également remove(T), bien qu'il soit permis de lancer un UnsupportedOperationException.
wchargin
Les tableaux implémentent des méthodes: ils implémentent toutes les méthodes de java.lang.Object.
mhsmith
59

Le tableau est un objet, mais ses éléments peuvent ne pas l'être. Le tableau peut contenir un type primitif comme int, auquel Iterable ne peut pas faire face. Du moins c'est ce que je pense.

Gareth Adamson
la source
3
Cela signifie que pour prendre en charge l' Iterableinterface, les tableaux primitifs doivent être spécialisés pour utiliser les classes wrapper. Cependant, rien de tout cela n'est vraiment grave, car les paramètres de type sont tous faux de toute façon.
thejoshwolfe
8
Cela n'empêcherait pas les tableaux d'objets d'implémenter Iterable. Cela n'empêcherait pas non plus les tableaux primitifs d'implémenter Iterable pour le type encapsulé.
Boann
1
La boxe automatique pourrait gérer cela
Tim Büthe
Je pense que c'est la bonne raison. Cela ne fonctionnera pas de manière satisfaisante pour les tableaux de types primitifs, tant que les génériques ne supporteront pas les types primitifs (par exemple List<int>au lieu de List<Integer>, etc.). Un hack pourrait être fait avec des wrappers mais avec une perte de performances - et plus important encore - si ce hack était fait, cela empêcherait de l'implémenter correctement en Java à l'avenir (par exemple, il int[].iterator()serait à jamais verrouillé pour revenir Iterator<Integer>plutôt que Iterator<int>). Peut-être que les prochains types de valeur + spécialisation générique pour Java (projet valhalla) rendront les tableaux implémentés Iterable.
Bjarke
16

Les tableaux devraient prendre en charge Iterable, ils ne le font tout simplement pas, pour la même raison que les tableaux .NET ne prennent pas en charge une interface qui permet un accès aléatoire en lecture seule par position (il n'y a pas d'interface définie comme standard). Fondamentalement, les frameworks ont souvent de petites lacunes ennuyeuses, que personne ne vaut le temps de corriger. Cela n'aurait pas d'importance si nous pouvions les réparer nous-mêmes de manière optimale, mais souvent nous ne pouvons pas.

MISE À JOUR: Pour être impartial, j'ai mentionné les tableaux .NET ne prenant pas en charge une interface qui prend en charge l'accès aléatoire par position (voir aussi mon commentaire). Mais dans .NET 4.5, cette interface exacte a été définie et est prise en charge par les tableaux et la List<T>classe:

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

Tout n'est pas encore tout à fait parfait car l'interface de liste mutable IList<T>n'hérite pas IReadOnlyList<T>:

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

Il y a peut-être un possible problème de compatibilité descendante avec un tel changement.

S'il y a des progrès sur des choses similaires dans les nouvelles versions de Java, je serais intéressé de savoir dans les commentaires! :)

Daniel Earwicker
la source
8
Les tableaux .NET implémentent l'interface IList
Tom Gillen
2
@Aphid - J'ai dit accès aléatoire en lecture seule . IList<T>expose les opérations de modification. Ce serait génial si IList<T>avait hérité quelque chose comme une IReadonlyList<T>interface, qui aurait juste eu Countet T this[int]et hérité IEnumerable<T>(qui soutient déjà l' énumération en lecture seule). Une autre grande chose serait une interface pour obtenir un énumérateur d'ordre inverse, que la Reverseméthode d'extension pourrait interroger (tout comme la Countméthode d'extension interroge pour ICollections'optimiser.)
Daniel Earwicker
Oui, ce serait bien mieux si les choses avaient été conçues de cette façon. L'interface IList définit les propriétés IsReadOnly et IsFixedSize, qui sont implémentées de manière appropriée par tableau. Cela m'a toujours semblé être une très mauvaise façon de le faire, car cela n'offre pas de temps de compilation pour vérifier que la liste qui vous est donnée est en fait en lecture seule, et je vois très rarement du code qui vérifie ces propriétés.
Tom Gillen
1
Les tableaux dans .NET implémentent IListet ICollectiondepuis .NET 1.1, IList<T>et ICollection<T>depuis .NET 2.0. C'est encore un autre cas où Java est loin derrière la concurrence.
Amir Abiri
@TomGillen: Mon plus gros problème avec IListest qu'il ne fournit pas plus d'attributs interrogeables. Je dirais qu'un ensemble approprié devrait inclure IsUpdateable, IsResizable, IsReadOnly, IsFixedSize et ExistingElementsAreImmutable pour les débutants. La question de savoir si le code d'une référence peut, sans transtypage, modifier une liste est distincte de la question de savoir si un code qui contient une référence à une liste qu'il n'est pas censé modifier peut en toute sécurité partager cette référence directement avec du code extérieur, ou si il peut supposer que certains aspects de la liste ne changeront jamais.
supercat du
14

Malheureusement, les tableaux ne sont pas « classassez». Ils n'implémentent pas l' Iterableinterface.

Alors que les tableaux sont maintenant des objets qui implémentent Clonable et Serializable, je pense qu'un tableau n'est pas un objet au sens normal du terme et n'implémente pas l'interface.

La raison pour laquelle vous pouvez les utiliser dans les boucles for-each est que Sun a ajouté du sucre syntatique pour les tableaux (c'est un cas particulier).

Puisque les tableaux ont commencé comme des «presque objets» avec Java 1, il serait beaucoup trop radical de changer pour en faire de vrais objets en Java.

jjnguy
la source
14
Pourtant, il y a du sucre pour la boucle for-each, alors pourquoi ne peut-il pas y avoir de sucre pour Iterable?
Michael Myers
8
@mmyers: Le sucre utilisé dans for-each est du sucre à la compilation . C'est beaucoup plus facile à faire que le sucre VM . Cela dit, les tableaux .NET sont nettement meilleurs dans ce domaine ...
Jon Skeet
12
Les tableaux peuvent implémenter des interfaces. Ils implémentent Cloneableet Serializables'interfacent.
notnoop le
34
Les tableaux java sont des objets dans tous les sens. Veuillez supprimer ce peu de désinformation. Ils n'implémentent tout simplement pas Iterable.
ykaganovich
5
Un tableau est un objet. Il prend en charge des méthodes utiles : P comme wait (), wait (n), wait (n, m), notify (), notifyAll (), finalize (), une implémentation inutile de toString () La seule méthode utile est getClass () .
Peter Lawrey le
1

Le compilateur traduit en fait le for eachsur un tableau en une simple forboucle avec une variable de compteur.

Compiler les éléments suivants

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

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

puis décompiler le fichier .class donne

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);
    }
}
das Keks
la source