É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:
Si vous voulez un Iterator
sur un tableau, vous pouvez utiliser l'une des implémentations directes au lieu d'encapsuler le tableau dans un 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 les 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<int>
, vous devez utiliser un Iterator<Integer>
à la place, ce qui entraînera beaucoup de procédures d'autoboxing et -unboxing si cela est soutenu par un int[]
.
Non, il n'y a pas de conversion. La machine virtuelle Java effectue simplement une itération sur le tableau à l'aide d'un index en arrière-plan.
Extrait de Effective Java 2nd Ed., Élément 46:
Notez qu'il n'y a aucune pénalité de performance liée à l'utilisation de la boucle for-each, même pour les tableaux. En fait, elle peut offrir un léger avantage en termes de performances par rapport à une boucle for ordinaire dans certaines circonstances, car elle ne calcule la limite de l'index de tableau qu'une seule fois.
Donc, vous ne pouvez pas obtenir un Iterator
pour un tableau (à moins bien sûr de le convertir d'abord en un List
).
Tableaux.asList (arr) .iterator ();
Ou écrivez la vôtre en implémentant l'interface ListIterator.
La collection de Google Guava Librarie s fournit cette fonction:
Iterator<String> it = Iterators.forArray(array);
Il faut préférer Guava sur la collection Apache (qui semble abandonnée).
Dans Java 8:
Arrays.stream(arr).iterator();
public class ArrayIterator<T> implements Iterator<T> {
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();
}
}
À proprement parler, vous ne pouvez pas obtenir d'itérateur du tableau primitif, car Iterator.next () ne peut renvoyer qu'un objet. Mais grâce à la magie de l'autoboxing, vous pouvez obtenir l'itérateur à l'aide de la méthode Arrays.asList () .
Iterator<Integer> 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<int[]>
. Utilisez plutôt Ints.asList()
de Guava .
Vous ne pouvez pas obtenir directement un itérateur pour un tableau.
Mais vous pouvez utiliser une liste, soutenue par votre tableau, et obtenir un ierator sur cette liste. Pour cela, votre tableau doit être un tableau Integer (au lieu d’un tableau int):
Integer[] arr={1,2,3};
List<Integer> arrAsList = Arrays.asList(arr);
Iterator<Integer> iter = arrAsList.iterator();
Note: ce n'est que de la théorie. Vous pouvez obtenir un itérateur comme celui-ci, mais je vous décourage de le faire. Les performances ne sont pas bonnes comparées à une itération directe sur le tableau avec la "syntaxe étendue pour".
Remarque 2: une construction de liste avec cette méthode ne prend pas en charge toutes les méthodes (car la liste est sauvegardée par le tableau qui a une taille fixe). Par exemple, la méthode "remove" de votre itérateur entraînera une exception.
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 de
Iterable
, alors la traduction est la suivante[...]
Sinon, l'expression a nécessairement un type de tableau,
T[]
. [[ LA MAGIE! ]]Laisser
L1 ... Lm
soit la séquence d'étiquettes (éventuellement vide) précédant immédiatement l'instruction améliorée pour.La déclaration for améliorée est équivalente à une déclaration for de base de la forme:
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 de tout autre identifiant (généré automatiquement ou non) qui se trouve dans la portée au moment où l’instruction améliorée pour se produit.
Let's javap
ça va:
public class ArrayForLoop {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
for (int i : arr)
System.out.println(i);
}
}
ensuite:
javac ArrayForLoop.Java
javap -v ArrayForLoop
main
avec un peu d’édition 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 tableau15
à 22
: prépare la boucle for. À 22 , enregistrez le nombre entier 0
de la pile à la position locale 4
. C'EST la variable de boucle.24
à 47
: la boucle. La variable de boucle est récupérée à 31
, et incrémenté à 44
. Quand il est égal à la longueur du tableau qui est stocké dans la variable locale 3 sur la vérification à 27
, la boucle se termine.Conclusion : équivaut à faire une boucle for explicite avec une variable d'index, sans itérateur.
Pour (2), Guava fournit exactement ce que vous voulez en tant que Int.asList () . Il existe un équivalent pour chaque type de primitive 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 au jeu, mais j’ai remarqué certains points essentiels qui ont été laissés de côté, notamment en ce qui concerne Java 8 et l’efficacité de Arrays.asList
.
Comme Ciro Santilli a souligné , il existe un utilitaire pratique pour examiner le code intermédiaire fourni avec le JDK: javap
. En utilisant cela, nous pouvons déterminer que les deux extraits de code suivants produisent un code identique à partir de Java 8u74:
int[] arr = {1, 2, 3};
for (int n : arr) {
System.out.println(n);
}
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);
}
}
Bien que cela ne fonctionne pas pour les primitives, il convient de noter que la conversion d'un tableau en liste avec Arrays.asList
N'affecte pas les performances de manière significative. L'impact sur la mémoire et les performances est presque incommensurable.
Arrays.asList
N'utilise pas une implémentation List normale facilement accessible en tant que classe. Il utilise Java.util.Arrays.ArrayList
, Ce qui n'est pas la même chose que Java.util.ArrayList
. C'est un très fin wrapper 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 <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
private static class ArrayList<E> extends AbstractList<E> 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'itérateur est à Java.util.AbstractList.Itr
. En ce qui concerne les itérateurs, c'est très simple. il appelle simplement get()
jusqu'à ce que size()
soit atteint, comme le ferait une boucle manuelle. C'est l'implémentation la plus simple et généralement la plus efficace d'un Iterator
pour un tableau.
Encore une fois, Arrays.asList
Ne crée pas un Java.util.ArrayList
. C'est beaucoup plus léger et approprié pour obtenir un itérateur avec des frais généraux négligeables.
Comme d'autres l'ont noté, Arrays.asList
Ne peut pas être utilisé sur des tableaux primitifs. Java 8 introduit plusieurs nouvelles technologies pour traiter des ensembles de données, dont plusieurs pourraient être utilisées pour extraire des itérateurs simples et relativement efficaces à partir de tableaux. Notez que si vous utilisez des génériques, vous êtes toujours prêt pour avoir le problème boxing-unboxing: vous aurez besoin de convertir int en integer, puis de nouveau en int. Alors que boxing/unboxing est généralement négligeable, il a un O(1) performance Dans ce cas, cela aurait un impact important et pourrait entraîner des problèmes avec de très grands tableaux ou avec des ordinateurs disposant de ressources très limitées (par exemple, SoC ).
Mon favori personnel pour tout type d'opération de transtypage/boxing de tableaux dans Java 8 est la nouvelle API de flux. Par exemple:
int[] arr = {1, 2, 3};
Iterator<Integer> iterator = Arrays.stream(arr).mapToObj(Integer::valueOf).iterator();
L'API de flux propose également des constructions permettant d'éviter le problème de la boxe, mais cela nécessite d'abandonner les itérateurs au profit des flux. Il existe des types de flux 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
. Ceci fournit le meilleur des deux mondes: compatibilité avec Iterator<T>
Via la boxe avec des méthodes pour éviter la boxe. PrimitiveIterator a trois interfaces intégrées qui l'étendent: OfInt, OfLong et OfDouble. Toutes trois cocheront si next()
est appelé, mais peut également renvoyer des primitives via des méthodes telles que nextInt()
. Nouveau code 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<Integer> without casting:
Iterator<Integer> 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, malheureusement, votre option la plus simple est beaucoup moins concise et impliquera presque certainement de la boxe:
final int[] arr = {1, 2, 3};
Iterator<Integer> iterator = new Iterator<Integer>() {
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<Integer> {
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 pouvez contourner le problème de la boxe ici en ajoutant vos propres méthodes pour obtenir des primitives, mais cela ne fonctionnerait qu'avec votre propre code interne.
Non ce n'est pas. Cependant, cela ne signifie pas que son insertion dans une liste va vous donner des performances pires si vous utilisez quelque chose de léger, tel que Arrays.asList
.
Je suis un étudiant récent, mais JE CROIS que l'exemple original avec int [] itère sur le tableau de primitives, mais pas en utilisant un objet 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 n’applique que les méthodes List à un tableau d’objets qui lui est attribué - mais pour tout autre type d’objet, y compris un tableau primitif, iterator (). Next () APPARENT simplement, vous remet la référence à c'est comme une liste avec un élément. Peut-on 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 (ou c’EST) une collection singleton. Donc ici asList () est sans importance pour le cas d’un tableau de primitives, mais déroutant. Je ne sais pas si j'ai raison, mais j'ai écrit un programme qui dit que je le suis.
Ainsi, cet exemple (où asList () ne fait pas ce que vous pensiez, et n’est donc pas quelque chose que vous utiliseriez réellement de cette façon) - j’espère que le code fonctionne mieux que mon marquage en tant que code, et, hé, regarde 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(String[] 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 à partir de 30h en utilisant Iterators
de Guava. Cependant, dans certains frameworks, j'obtiens null au lieu d'un tableau vide, et Iterators.forArray(array)
ne gère pas très bien. Alors je suis venu avec cette méthode d'assistance, que vous pouvez appeler avec Iterator<String> it = emptyIfNull(array);
public static <F> UnmodifiableIterator<F> emptyIfNull(F[] array) {
if (array != null) {
return Iterators.forArray(array);
}
return new UnmodifiableIterator<F>() {
public boolean hasNext() {
return false;
}
public F next() {
return null;
}
};
}