Je dois manquer quelque chose ici.
Dans Java 5, l’instruction "boucle for for each" (également appelée boucle améliorée pour) a été introduite. Il semble qu’elle ait été introduite principalement pour parcourir Collections . Toute classe de collection (ou conteneur) qui implémente l'interface Iterable
est éligible pour l'itération à l'aide de " for-each loop ". Peut-être pour des raisons historiques, les baies Java n'implémentent pas l'interface Iterable. Mais comme elles étaient/sont omniprésentes, javac
accepterait l'utilisation de pour -chaque boucle sur les tableaux (générant du bytecode équivalent à une boucle for traditionnelle).
Dans Java 8, la forEach
méthode a été ajoutée à l'interface Iterable
en tant que La méthode default permet de passer des expressions lambda à des collections (tout en itérant) (par exemple, list.forEach(System.out::println)
). Mais, encore une fois, les tableaux ne bénéficient pas de ce traitement. il existe des solutions de contournement).
Existe-t-il des raisons techniques pour lesquelles javac ne pourrait pas être amélioré pour accepter les tableaux dans forEach, tout comme il les accepte dans la boucle for améliorée? Il semble que la génération de code serait possible sans exiger que les tableaux implémentent Iterable
. Suis-je naïf?
Ceci est particulièrement important pour un nouveau venu dans la langue qui utilise plutôt naturellement des tableaux en raison de leur facilité syntaxique. Il n'est pas naturel de passer aux listes et d'utiliser Arrays.asList(1, 2, 3)
.
Il existe de nombreux cas particuliers dans le langage Java et dans la machine virtuelle Java pour les tableaux. Les tableaux ont une API, mais elle est à peine visible. C'est comme si les tableaux étaient déclarés avoir:
implements Cloneable, Serializable
public final int length
public T[] clone()
où T
est le type de composant du tableauCependant, ces déclarations ne sont visibles dans aucun code source, où que ce soit. Voir JLS 4.10. et JLS 10.7 pour des explications. Cloneable
et Serializable
sont visibles par réflexion et sont renvoyés par un appel à
Object[].class.getInterfaces()
Peut-être étonnamment, le champ length
et la méthode clone()
ne sont pas visibles de manière réfléchie. Le champ length
n'est pas du tout un champ. son utilisation devient un bytecode spécial arraylength
. Un appel à clone()
donne lieu à un appel de méthode virtuelle, mais si le récepteur est un type de tableau, cette opération est gérée spécialement par la JVM.
Cependant, les classes de tableaux n'implémentent pas l'interface Iterable
.
Lorsque la boucle for améliorée ("for-each") a été ajoutée dans Java SE 5, elle prenait en charge deux cas différents pour l'expression de droite: un Iterable
ou un type de tableau ( JLS 14.14.2 ). La raison en est que les instances et tableaux de Iterable
sont gérés de manière complètement différente par l’instruction Enhanced-for. Cette section du fichier JLS donne la liste complète. traitement, mais plus simplement, la situation est la suivante.
Pour un Iterable<T> iterable
, Le code
for (T t : iterable) {
<loop body>
}
est le sucre syntaxique pour
for (Iterator<T> iterator = iterable.iterator(); iterator.hasNext(); ) {
t = iterator.next();
<loop body>
}
Pour un tableau T[]
, Le code
for (T t : array) {
<loop body>
}
est le sucre syntaxique pour
int len = array.length;
for (int i = 0; i < len; i++) {
t = array[i];
<loop body>
}
Maintenant, pourquoi cela a-t-il été fait de cette façon? Il serait certainement possible que les matrices implémentent Iterable
, puisqu'elles implémentent déjà d'autres interfaces. Il serait également possible pour le compilateur de synthétiser une implémentation Iterator
soutenue par un tableau. (Il existe un précédent. Le compilateur synthétise déjà les méthodes statiques values()
et valueOf()
qui sont automatiquement ajoutées à chaque classe enum
, comme décrit dans la section JLS 8.9. .)
Mais les tableaux sont une construction de très bas niveau, et l'accès à un tableau par une valeur int
devrait être une opération extrêmement peu coûteuse. Il est assez idiomatique d'exécuter un index de boucle de 0
À la longueur d'un tableau, en l'incrémentant de un à chaque fois. La boucle améliorée sur un tableau fait exactement cela. Si la boucle améliorée sur un tableau était mise en œuvre en utilisant le protocole Iterable
, je pense que la plupart des gens seraient désagréablement surpris de découvrir que la boucle sur un tableau impliquait un appel de méthode initial et une allocation de mémoire (création du Iterator
), suivi de deux appels de méthode par itération de boucle.
Ainsi, lorsque des méthodes par défaut ont été ajoutées à Iterable
dans Java 8, cela n’affectait pas du tout les tableaux.
Comme d'autres l'ont noté, si vous avez un tableau de int
, long
, double
ou de type référence, vous pouvez le transformer en flux en utilisant l'un des Arrays.stream()
appelle. Cela donne accès à map()
, filter()
, forEach()
, etc.
Ce serait bien si les cas spéciaux de la Java et de la JVM pour les tableaux) étaient remplacés par real (avec la résolution de plusieurs problèmes liés au tableau, tels que la mauvaise gestion de tableaux à 2 dimensions, la limitation de longueur de 2 ^ 31, etc.). C’est le sujet de l’enquête "Tableaux 2.0" menée par John Rose Voir l'exposé de John à JVMLS 2012 ( vidéo , diapositives ). Les idées pertinentes pour cette discussion incluent l'introduction d'une interface réelle pour les tableaux, permettant aux bibliothèques d'interposer des éléments accès, pour prendre en charge des opérations supplémentaires telles que le découpage en tranches et la copie, etc.
Notez que tout ceci est une enquête et un travail futur. Aucune amélioration de ces tableaux n’a été validée dans la feuille de route Java), quelle que soit la version, à la date de rédaction de ce document (2016-02-23).
Supposons que le code spécial soit ajouté au Java pour gérer forEach
. Plusieurs questions similaires peuvent ensuite être posées. Pourquoi ne pouvons-nous pas écrire myArray.fill(0)
? _ Ou myArray.copyOfRange(from, to)
? ou myArray.sort()
? myArray.binarySearch()
? myArray.stream()
? Pratiquement toutes les méthodes statiques de l'interface Arrays
peuvent être converties en méthode de la "classe de tableau". Pourquoi les développeurs JDK devraient-ils s’arrêter sur myArray.forEach()
? Notez cependant que chacune de ces méthodes doit être ajoutée non seulement à la spécification classlib, mais également à Java Langage Une spécification beaucoup plus stable et conservatrice, ce qui impliquerait non seulement que l’implémentation de telles méthodes devienne partie intégrante de la spécification, mais que des classes telles que Java.util.function.Consumer
Soient explicitement mentionnées dans JLS (ce qui est l’argument de forEach
méthode. Notez également que de nouveaux consommateurs seraient nécessaires d’ajouter à la bibliothèque standard comme FloatConsumer
, ByteConsumer
, etc. pour les types de tableaux correspondants. se réfère à la types en dehors du paquet Java.lang
(à quelques exceptions notables comme Java.util.Iterator
). Cela implique une couche de stabilité. Le changement proposé est trop radical pour le langage Java.
Notez également qu’à l’heure actuelle, nous avons une méthode qui peut être appelée directement pour les tableaux (et dont l’implémentation diffère de la méthode Java.lang.Object
): C’est la méthode clone()
. En fait, il ajoute des parties sales dans javac et même dans la machine virtuelle Java, car il doit être manipulé spécialement partout. Cela provoque des bogues (par exemple, les références aux méthodes ont été gérées de manière incorrecte dans Java 8 JDK-8056051). L'ajout d'une complexité similaire dans javac peut introduire des bogues encore plus similaires.
Une telle fonctionnalité sera probablement implémentée dans un avenir pas si proche dans le cadre de initiative Arrays 2. . L’idée est d’introduire quelques super-classes pour les tableaux qui seront situés dans la bibliothèque de classes. Ainsi, de nouvelles méthodes pourraient être ajoutées simplement en écrivant le code normal Java) sans modifier javac/JVM. les caractéristiques matérielles, car les tableaux sont toujours traités spécialement en Java, et, autant que je sache, on ne sait pas encore si elles seront implémentées et quand.