Comment fonctionne la fonction améliorée pour les tableaux pour les tableaux et comment obtenir un iterator pour un tableau?

Étant donné l’extrait de code suivant:

int[] arr = {1, 2, 3}; for (int i : arr) System.out.println(i); 

J’ai les questions suivantes:

  1. Comment fonctionne la boucle ci-dessus pour chaque boucle?
  2. Comment puis-je obtenir un iterator pour un tableau en Java?
  3. Le tableau est-il converti en une liste pour obtenir l’iterator?

Si vous voulez un Iterator sur un tableau, vous pouvez utiliser l’une des implémentations directes au lieu d’encapsuler le tableau dans une List . Par exemple:

Apache Commons Collections ArrayIterator

Ou, celui-ci, si vous souhaitez utiliser des génériques:

com.Ostermiller.util.ArrayIterator

Notez que si vous voulez avoir un Iterator sur des types primitifs, vous ne pouvez pas, car un type primitif ne peut pas être un paramètre générique. Par exemple, si vous voulez un Iterator , vous devez utiliser un Iterator place, ce qui se traduira par une mise en boîte automatique et un -unboxing si cela est soutenu par un int[] .

Non, il n’y a pas de conversion. La JVM effectue simplement une itération sur le tableau en utilisant un index en arrière-plan.

Citation de Effective Java 2nd Ed., Article 46:

Notez qu’il n’y a pas de pénalité de performance pour l’utilisation de la boucle for-each, même pour les tableaux. En fait, il peut offrir un léger avantage de performance par rapport à une boucle for ordinaire dans certaines circonstances, car il ne calcule la limite de l’index du tableau qu’une seule fois.

Vous ne pouvez donc pas obtenir un Iterator pour un tableau (sauf bien sûr en le convertissant d’abord en une List ).

Arrays.asList (arr) .iterator ();

Ou écrivez votre propre implémentation de l’interface ListIterator.

La collection de Google Guava Librarie offre une telle fonction:

 Iterator it = Iterators.forArray(array); 

On devrait préférer Guava à la collection Apache (qui semble être abandonnée).

Dans Java 8:

 Arrays.stream(arr).iterator(); 
 public class ArrayIterator implements Iterator { private T array[]; private int pos = 0; public ArrayIterator(T anArray[]) { array = anArray; } public boolean hasNext() { return pos < array.length; } public T next() throws NoSuchElementException { if (hasNext()) return array[pos++]; else throw new NoSuchElementException(); } public void remove() { throw new UnsupportedOperationException(); } } 

Ssortingctement parlant, vous ne pouvez pas obtenir un iterator du tableau primitif, car Iterator.next () ne peut que renvoyer un object. Mais grâce à la magie de l’autoboxing, vous pouvez obtenir l’iterator à l’aide de la méthode Arrays.asList () .

 Iterator it = Arrays.asList(arr).iterator(); 

La réponse ci-dessus est fausse, vous ne pouvez pas utiliser Arrays.asList() sur un tableau primitif, cela retournerait un List . Utilisez plutôt Ints.asList() Guava .

Vous ne pouvez pas obtenir directement un iterator pour un tableau.

Mais vous pouvez utiliser une liste, sauvegardée par votre tableau, et obtenir un indicateur sur cette liste. Pour cela, votre tableau doit être un tableau Integer (au lieu d’un tableau int):

 Integer[] arr={1,2,3}; List arrAsList = Arrays.asList(arr); Iterator iter = arrAsList.iterator(); 

Note: ce n’est que de la théorie. Vous pouvez obtenir un iterator comme celui-ci, mais je vous déconseille de le faire. Les performances ne sont pas bonnes comparées à une itération directe sur le tableau avec la “syntaxe étendue”.

Note 2: une construction de liste avec cette méthode ne supporte pas toutes les méthodes (puisque la liste est soutenue par le tableau qui a une taille fixe). Par exemple, la méthode “remove” de votre iterator entraînera une exception.

Comment fonctionne la boucle ci-dessus pour chaque boucle?

Comme beaucoup d’autres fonctionnalités de tableau, le JSL mentionne explicitement les tableaux et leur donne des propriétés magiques. JLS 7 14.14.2 :

 EnhancedForStatement: for ( FormalParameter : Expression ) Statement 

[…]

Si le type d’expression est un sous-type d’ Iterable , la traduction est la suivante

[…]

Sinon, l’expression a nécessairement un type de tableau, T[] . [[ LA MAGIE! ]]

Soit L1 ... Lm la séquence (éventuellement vide) d’étiquettes précédant immédiatement l’instruction améliorée.

L’instruction améliorée est équivalente à une instruction de base du formulaire:

 T[] #a = Expression; L1: L2: ... Lm: for (int #i = 0; #i < #a.length; #i++) { VariableModifiersopt TargetType Identifier = #a[#i]; Statement } 

#a et #i sont des identifiants générés automatiquement qui sont distincts des autres identificateurs (générés automatiquement ou non) qui sont au point où l'instruction améliorée est produite.

Le tableau est-il converti en une liste pour obtenir l'iterator?

javap -le:

 public class ArrayForLoop { public static void main(Ssortingng[] args) { int[] arr = {1, 2, 3}; for (int i : arr) System.out.println(i); } } 

puis:

 javac ArrayForLoop.java javap -v ArrayForLoop 

méthode main avec un peu de modification pour faciliter la lecture:

  0: iconst_3 1: newarray int 3: dup 4: iconst_0 5: iconst_1 6: iastore 7: dup 8: iconst_1 9: iconst_2 10: iastore 11: dup 12: iconst_2 13: iconst_3 14: iastore 15: astore_1 16: aload_1 17: astore_2 18: aload_2 19: arraylength 20: istore_3 21: iconst_0 22: istore 4 24: iload 4 26: iload_3 27: if_icmpge 50 30: aload_2 31: iload 4 33: iaload 34: istore 5 36: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 39: iload 5 41: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 44: iinc 4, 1 47: goto 24 50: return 

Panne:

  • 0 à 14 : créer le tableau
  • 15 à 22 : préparer la boucle for. En 22 , stockez l'entier 0 de la stack dans la position locale 4 . C’est la variable de boucle.
  • 24 à 47 : la boucle. La variable de boucle est extraite à 31 et incrémentée à 44 . Quand elle est égale à la longueur du tableau qui est stockée dans la variable locale 3 lors de la vérification à 27 , la boucle se termine.

Conclusion : c'est la même chose que de faire une boucle explicite avec une variable d'index, sans aucun iterator.

Pour (2), Guava fournit exactement ce que vous voulez comme Int.asList () . Il y a un équivalent pour chaque type primitif dans la classe associée, par exemple, Booleans pour boolean , etc.

  int[] arr={1,2,3}; for(Integer i : Ints.asList(arr)) { System.out.println(i); } 

Je suis un peu en retard sur le jeu, mais j’ai remarqué quelques points clés qui ont été Arrays.asList , en particulier en ce qui concerne Java 8 et l’efficacité d’ Arrays.asList .

1. Comment fonctionne la boucle for-each?

Comme l’a souligné Ciro Santilli , il existe un utilitaire pratique pour examiner le bytecode javap avec le JDK: javap . En utilisant cela, nous pouvons déterminer que les deux extraits de code suivants produisent un bytecode identique à celui de Java 8u74:

Pour chaque boucle:

 int[] arr = {1, 2, 3}; for (int n : arr) { System.out.println(n); } 

Pour la boucle:

 int[] arr = {1, 2, 3}; { // These extra braces are to limit scope; they do not affect the bytecode int[] iter = arr; int length = iter.length; for (int i = 0; i < length; i++) { int n = iter[i]; System.out.println(n); } } 

2. Comment puis-je obtenir un iterator pour un tableau en Java?

Bien que cela ne fonctionne pas pour les primitives, il convient de noter que la conversion d'un tableau en une liste avec Arrays.asList n'a pas d'impact significatif sur les performances. L'impact sur la mémoire et la performance est presque incommensurable.

Arrays.asList n'utilise pas une implémentation de liste normale facilement accessible en tant que classe. Il utilise java.util.Arrays.ArrayList , différent de java.util.ArrayList . C'est un wrapper très fin autour d'un tableau et ne peut pas être redimensionné. En regardant le code source de java.util.Arrays.ArrayList , nous pouvons voir qu'il est conçu pour être fonctionnellement équivalent à un tableau. Il n'y a presque pas de frais généraux. Notez que j'ai omis tout sauf le code le plus pertinent et ajouté mes propres commentaires.

 public class Arrays { public static  List asList(T... a) { return new ArrayList<>(a); } private static class ArrayList extends AbstractList implements RandomAccess, java.io.Serializable { private final E[] a; ArrayList(E[] array) { a = Objects.requireNonNull(array); } @Override public int size() { return a.length; } @Override public E get(int index) { return a[index]; } @Override public E set(int index, E element) { E oldValue = a[index]; a[index] = element; return oldValue; } } } 

L'iterator se trouve à java.util.AbstractList.Itr . En ce qui concerne les iterators, c'est très simple. il appelle juste get() jusqu'à ce que size() soit atteint, un peu comme le ferait un manuel pour la boucle. C'est l'implémentation la plus simple et la plus efficace d'un Iterator pour un tableau.

Encore une fois, Arrays.asList ne crée pas de java.util.ArrayList . Il est beaucoup plus léger et adapté pour obtenir un iterator avec des frais généraux négligeables.

Tableaux primitifs

Comme d'autres l'ont noté, Arrays.asList ne peut pas être utilisé sur les tableaux primitifs. Java 8 introduit plusieurs nouvelles technologies pour traiter des collections de données, dont plusieurs pourraient être utilisées pour extraire des iterators simples et relativement efficaces à partir de tableaux. Notez que si vous utilisez des génériques, vous rencontrerez toujours le problème de boxing-unboxing: vous devrez convertir de int en Integer, puis de nouveau en int. Bien que la boxing / unboxing soit généralement négligeable, elle a un impact sur les performances O (1) dans ce cas et peut entraîner des problèmes avec de très grandes baies ou sur des ordinateurs avec des ressources très limitées ( SoC ).

Mon préféré pour toute opération de casting / boxing de tableaux dans Java 8 est la nouvelle API de stream. Par exemple:

 int[] arr = {1, 2, 3}; Iterator iterator = Arrays.stream(arr).mapToObj(Integer::valueOf).iterator(); 

L'API de stream offre également des constructions pour éviter le problème de la boxe en premier lieu, mais cela nécessite d'abandonner les iterators en faveur des stream. Il existe des types de stream dédiés pour int, long et double (IntStream, LongStream et DoubleStream, respectivement).

 int[] arr = {1, 2, 3}; IntStream stream = Arrays.stream(arr); stream.forEach(System.out::println); 

Fait intéressant, Java 8 ajoute également java.util.PrimitiveIterator . Cela fournit le meilleur des deux mondes: la compatibilité avec Iterator via la boxe avec des méthodes pour éviter la boxe. PrimitiveIterator possède trois interfaces intégrées qui l'étendent: OfInt, OfLong et OfDouble. Tous les trois seront encadrés si next() est appelé mais peut également renvoyer des primitives via des méthodes telles que nextInt() . Un code plus récent conçu pour Java 8 devrait éviter d'utiliser next() moins que la boxe ne soit absolument nécessaire.

 int[] arr = {1, 2, 3}; PrimitiveIterator.OfInt iterator = Arrays.stream(arr); // You can use it as an Iterator without casting: Iterator example = iterator; // You can obtain primitives while iterating without ever boxing/unboxing: while (iterator.hasNext()) { // Would result in boxing + unboxing: //int n = iterator.next(); // No boxing/unboxing: int n = iterator.nextInt(); System.out.println(n); } 

Si vous n'êtes pas encore sur Java 8, votre option la plus simple est malheureusement beaucoup moins concise et impliquera certainement la boxe:

 final int[] arr = {1, 2, 3}; Iterator iterator = new Iterator() { int i = 0; @Override public boolean hasNext() { return i < arr.length; } @Override public Integer next() { if (!hasNext()) { throw new NoSuchElementException(); } return arr[i++]; } }; 

Ou si vous voulez créer quelque chose de plus réutilisable:

 public final class IntIterator implements Iterator { private final int[] arr; private int i = 0; public IntIterator(int[] arr) { this.arr = arr; } @Override public boolean hasNext() { return i < arr.length; } @Override public Integer next() { if (!hasNext()) { throw new NoSuchElementException(); } return arr[i++]; } } 

Vous pourriez contourner le problème de la boxe en ajoutant vos propres méthodes pour obtenir des primitives, mais cela ne fonctionnerait qu'avec votre propre code interne.

3. Le tableau est-il converti en une liste pour obtenir l'iterator?

Non, ce n'est pas Cependant, cela ne signifie pas que le fait de l'enrouler dans une liste vous donnera de moins bonnes performances, à condition que vous Arrays.asList quelque chose de léger comme Arrays.asList .

Je suis un étudiant récent, mais je crois que l’exemple original avec int [] est itéré sur le tableau des primitives, mais pas en utilisant un object Iterator. Il a simplement la même syntaxe (similaire) avec des contenus différents,

 for (primitive_type : array) { } for (object_type : iterableObject) { } 

Arrays.asList () APPARENTly applique simplement les méthodes List à un tableau d’objects qui lui est donné – mais pour tout autre type d’object, y compris un tableau primitif, iterator (). Next () APPARENTly vous remet la référence à l’object d’origine, comme une liste avec un élément. Pouvons-nous voir le code source pour cela? Ne préféreriez-vous pas une exception? Ça ne fait rien. Je suppose (c’est GUESS) que c’est comme une collection singleton. Donc, ici, asList () est sans rapport avec le cas avec un tableau de primitives, mais confus. Je ne sais pas, j’ai raison, mais j’ai écrit un programme qui dit que je suis.

Ainsi, cet exemple (où fondamentalement asList () ne fait pas ce que vous pensiez qu’il ferait, et n’est donc pas quelque chose que vous utiliseriez de cette façon) – J’espère que le code fonctionne mieux que mon marquage en tant que code et Hé, regardez cette dernière ligne:

 // Java(TM) SE Runtime Environment (build 1.6.0_19-b04) import java.util.*; public class Page0434Ex00Ver07 { public static void main(Ssortingng[] args) { int[] ii = new int[4]; ii[0] = 2; ii[1] = 3; ii[2] = 5; ii[3] = 7; Arrays.asList(ii); Iterator ai = Arrays.asList(ii).iterator(); int[] i2 = (int[]) ai.next(); for (int i : i2) { System.out.println(i); } System.out.println(Arrays.asList(12345678).iterator().next()); } } 

J’aime la réponse de 30th en utilisant les Iterators de goyave. Cependant, à partir de certains frameworks, je suis nul au lieu d’un tableau vide et Iterators.forArray(array) ne le gère pas correctement. J’ai donc trouvé cette méthode d’assistance, que vous pouvez appeler avec Iterator it = emptyIfNull(array);

 public static  UnmodifiableIterator emptyIfNull(F[] array) { if (array != null) { return Iterators.forArray(array); } return new UnmodifiableIterator() { public boolean hasNext() { return false; } public F next() { return null; } }; }