Gegeben das folgende Code-Snippet:
int[] arr = {1, 2, 3};
for (int i : arr)
System.out.println(i);
Ich habe folgende Fragen:
- Wie funktioniert die obige Schleife für jede Schleife?
- Wie bekomme ich einen Iterator für ein Array in Java?
- Wird das Array in eine Liste konvertiert, um den Iterator zu erhalten?
Antworten:
Wenn Sie ein
Iterator
Over-Array wünschen , können Sie eine der direkten Implementierungen verwenden, anstatt das Array in ein Array zu verpackenList
. Zum Beispiel:Apache Commons-Sammlungen
ArrayIterator
Oder dieses, wenn Sie Generika verwenden möchten:
com.Ostermiller.util.ArrayIterator
Beachten Sie
Iterator
, dass dies nicht möglich ist , wenn Sie einen überprimitiven Typ haben möchten , da ein primitiver Typ kein generischer Parameter sein kann. Wenn Sie beispielsweise eine möchtenIterator<int>
, müssen SieIterator<Integer>
stattdessen eine verwenden, was zu viel Autoboxing und -unboxing führt, wenn dies durch eine unterstützt wirdint[]
.quelle
ArrayIt
; Annahme aller Bibliotheken von Drittanbietern.Nein, es erfolgt keine Konvertierung. Die JVM durchläuft das Array nur mithilfe eines Index im Hintergrund.
Zitat aus Effective Java 2nd Ed., Punkt 46:
Sie können also kein
Iterator
für ein Array erhalten (es sei denn, Sie konvertieren es in einList
erstes).quelle
for
. @Emil, du kannst nicht.Arrays.asList (arr) .iterator ();Oder schreiben Sie Ihre eigene, implementierende ListIterator-Schnittstelle.
quelle
Arrays.asList()
kann nicht eineint[]
, nurObject[]
und Unterklassen nehmen.List<int[]>
und damit einIterator<int[]>
, kein a zurückList<Integer>
!Arrays.asList()
Verwendet das eigentliche Array und umschließt es mit einer Klasse, die es an die Listenschnittstelle anpasst. Mit Ausnahme des Problems "Nicht unterstützende Grundelemente" (und für Personen, die in einer nicht primitiven Situation mit demselben Problem hierher gekommen sind) ist dies tatsächlich eine gute Lösung.Die Sammlung von Google Guava Librarie bietet folgende Funktionen:
Man sollte Guave der Apache-Sammlung vorziehen (die aufgegeben zu sein scheint).
quelle
int[]
) handelt, dann funktioniert dies nicht mehr :( da wir nicht spezifizieren könnenIterator<int>
oderIterator<Integer>
In Java 8:
quelle
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(); } }
quelle
Genau genommen können Sie keinen Iterator des primitiven Arrays erhalten, da Iterator.next () nur ein Objekt zurückgeben kann. Durch die Magie des Autoboxing können Sie den Iterator mithilfe der Arrays.asList () -Methode abrufen .Die obige Antwort ist falsch. Sie können sie nicht
Arrays.asList()
für ein primitives Array verwenden. Sie würde a zurückgebenList<int[]>
. Verwenden Guava ‚sInts.asList()
statt.quelle
Sie können keinen Iterator für ein Array direkt abrufen.
Sie können jedoch eine Liste verwenden, die von Ihrem Array unterstützt wird, und einen Iierator in diese Liste aufnehmen. Dazu muss Ihr Array ein Integer- Array sein (anstelle eines int-Arrays):
Integer[] arr={1,2,3}; List<Integer> arrAsList = Arrays.asList(arr); Iterator<Integer> iter = arrAsList.iterator();
Hinweis: Es ist nur Theorie. Sie können einen solchen Iterator bekommen, aber ich rate Ihnen davon ab. Die Leistung ist im Vergleich zu einer direkten Iteration auf dem Array mit der "erweiterten Syntax" nicht gut.
Hinweis 2: Ein Listenkonstrukt mit dieser Methode unterstützt nicht alle Methoden (da die Liste von einem Array mit fester Größe unterstützt wird). Beispielsweise führt die Methode "remove" Ihres Iterators zu einer Ausnahme.
quelle
Arrays.asList
kümmert sich implizit darum, indem die Werte im Array eingerahmt werden. Daher ist das Ergebnis wirklich ist einList<Integer>
.Wie funktioniert die obige Schleife für jede Schleife?
Wie viele andere Array-Funktionen erwähnt die JSL Arrays explizit und verleiht ihnen magische Eigenschaften. JLS 7 14.14.2 :
EnhancedForStatement: for ( FormalParameter : Expression ) Statement
T[] #a = Expression; L1: L2: ... Lm: for (int #i = 0; #i < #a.length; #i++) { VariableModifiersopt TargetType Identifier = #a[#i]; Statement }
Wird das Array in eine Liste konvertiert, um den Iterator zu erhalten?
Lass es uns
javap
tun:public class ArrayForLoop { public static void main(String[] args) { int[] arr = {1, 2, 3}; for (int i : arr) System.out.println(i); } }
dann:
main
Methode mit ein wenig Bearbeitung, um das Lesen zu erleichtern: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
Nervenzusammenbruch:
0
to14
: Erstellen Sie das Array15
to22
: bereite dich auf die for-Schleife vor. Speichern Sie bei 22 die Ganzzahl0
vom Stapel in die lokale Position4
. DAS ist die Schleifenvariable.24
zu47
: der Schleife. Die Schleifenvariable wird um abgerufen31
und um erhöht44
. Wenn es der Array-Länge entspricht, die bei der Prüfung in in der lokalen Variablen 3 gespeichert ist27
, endet die Schleife.Schlussfolgerung : Dies entspricht einer expliziten for-Schleife mit einer Indexvariablen, an der keine Itereatoren beteiligt sind.
quelle
Ich bin etwas spät dran, aber ich habe einige wichtige Punkte bemerkt, die ausgelassen wurden, insbesondere in Bezug auf Java 8 und die Effizienz von
Arrays.asList
.1. Wie funktioniert die for-each-Schleife?
Wie Ciro Santilli hervorhob , gibt es ein praktisches Dienstprogramm zum Überprüfen des Bytecodes, der mit dem JDK geliefert wird :
javap
. Auf diese Weise können wir feststellen, dass die folgenden zwei Codefragmente identischen Bytecode wie in Java 8u74 erzeugen:Für jede Schleife:
int[] arr = {1, 2, 3}; for (int n : arr) { System.out.println(n); }
For-Schleife:
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. Wie bekomme ich einen Iterator für ein Array in Java?
Dies funktioniert zwar nicht für Grundelemente, es sollte jedoch beachtet werden, dass das Konvertieren eines Arrays in eine Liste mit
Arrays.asList
keine wesentlichen Auswirkungen auf die Leistung hat. Die Auswirkungen auf Speicher und Leistung sind nahezu unermesslich.Arrays.asList
verwendet keine normale List-Implementierung, auf die als Klasse leicht zugegriffen werden kann. Es verwendetjava.util.Arrays.ArrayList
, was nicht dasselbe ist wiejava.util.ArrayList
. Es ist ein sehr dünner Wrapper um ein Array und kann nicht in der Größe geändert werden. Wennjava.util.Arrays.ArrayList
wir uns den Quellcode ansehen, sehen wir, dass er funktional einem Array entspricht. Es gibt fast keinen Overhead. Beachten Sie, dass ich alle bis auf den relevantesten Code weggelassen und meine eigenen Kommentare hinzugefügt habe.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; } } }
Der Iterator ist bei
java.util.AbstractList.Itr
. Was Iteratoren betrifft, ist es sehr einfach; Es wird nur aufgerufen,get()
bissize()
es erreicht ist, ähnlich wie es ein Handbuch für die Schleife tun würde. Es ist die einfachste und normalerweise effizienteste Implementierung einesIterator
für ein Array.Auch
Arrays.asList
hier wird kein erstelltjava.util.ArrayList
. Es ist viel leichter und geeignet, um einen Iterator mit vernachlässigbarem Overhead zu erhalten.Primitive Arrays
Wie andere angemerkt haben,
Arrays.asList
kann nicht für primitive Arrays verwendet werden. Java 8 führt mehrere neue Technologien für den Umgang mit Datensammlungen ein, von denen einige verwendet werden könnten, um einfache und relativ effiziente Iteratoren aus Arrays zu extrahieren. Beachten Sie, dass bei Verwendung von Generika immer das Problem des Boxens und Entpackens auftritt: Sie müssen von int nach Integer und dann zurück nach int konvertieren. Während das Boxen / Entpacken normalerweise vernachlässigbar ist, hat es in diesem Fall einen Einfluss auf die O (1) -Leistung und kann zu Problemen mit sehr großen Arrays oder auf Computern mit sehr begrenzten Ressourcen (z. B. SoC ) führen.Mein persönlicher Favorit für jede Art von Array-Casting / Boxing-Operation in Java 8 ist die neue Stream-API. Zum Beispiel:
int[] arr = {1, 2, 3}; Iterator<Integer> iterator = Arrays.stream(arr).mapToObj(Integer::valueOf).iterator();
Die Streams-API bietet auch Konstrukte, um das Boxproblem zu vermeiden. Dazu müssen jedoch Iteratoren zugunsten von Streams aufgegeben werden. Es gibt dedizierte Stream-Typen für int, long und double (IntStream, LongStream bzw. DoubleStream).
int[] arr = {1, 2, 3}; IntStream stream = Arrays.stream(arr); stream.forEach(System.out::println);
Interessanterweise fügt Java 8 auch hinzu
java.util.PrimitiveIterator
. Dies bietet das Beste aus beiden Welten: Kompatibilität mitIterator<T>
Via-Boxen sowie Methoden zur Vermeidung von Boxen. PrimitiveIterator verfügt über drei integrierte Schnittstellen, die es erweitern: OfInt, OfLong und OfDouble. Alle drei werden boxen, wennnext()
aufgerufen wird, können aber auch Primitive über Methoden wie zurückgebennextInt()
. Neuerer Code, der für Java 8 entwickelt wurde, sollte die Verwendung vermeiden, esnext()
sei denn, Boxen ist unbedingt erforderlich.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); }
Wenn Sie noch nicht mit Java 8 arbeiten, ist Ihre einfachste Option leider viel weniger prägnant und wird mit ziemlicher Sicherheit das Boxen beinhalten:
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++]; } };
Oder wenn Sie etwas wiederverwendbareres erstellen möchten:
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++]; } }
Sie könnten das Boxproblem hier umgehen, indem Sie Ihre eigenen Methoden zum Abrufen von Grundelementen hinzufügen, aber es würde nur mit Ihrem eigenen internen Code funktionieren.
3. Wird das Array in eine Liste konvertiert, um den Iterator zu erhalten?
Nein ist es nicht. Dies bedeutet jedoch nicht, dass das Einbinden in eine Liste zu einer schlechteren Leistung führt, vorausgesetzt, Sie verwenden etwas Leichtes wie
Arrays.asList
.quelle
mapToObj(Integer::new)
durchmapToObj(Integer::valueOf)
Für (2) bietet Guava genau das, was Sie als Int.asList () wollen . Für jeden primitiven Typ in der zugehörigen Klasse gibt es ein Äquivalent, z. B.
Booleans
fürboolean
usw.int[] arr={1,2,3}; for(Integer i : Ints.asList(arr)) { System.out.println(i); }
quelle
Ich bin ein neuer Student, aber ich glaube, dass das ursprüngliche Beispiel mit int [] über das Primitiv-Array iteriert, aber nicht mithilfe eines Iterator-Objekts. Es hat lediglich die gleiche (ähnliche) Syntax mit unterschiedlichen Inhalten,
for (primitive_type : array) { } for (object_type : iterableObject) { }
Arrays.asList () wendet anscheinend nur List-Methoden auf ein Objektarray an, das angegeben ist - für jede andere Art von Objekt, einschließlich eines primitiven Arrays, iterator (). Next () gibt Ihnen anscheinend nur den Verweis auf das ursprüngliche Objekt und behandelt ihn es als Liste mit einem Element. Können wir den Quellcode dafür sehen? Würdest du nicht eine Ausnahme bevorzugen? Keine Ursache. Ich denke (das ist GUESS), dass es wie eine Singleton-Sammlung ist (oder IST). Hier ist asList () für den Fall mit einem primitiven Array irrelevant, aber verwirrend. Ich weiß nicht, ob ich Recht habe, aber ich habe ein Programm geschrieben, das besagt, dass ich es bin.
Daher dieses Beispiel (in dem asList () im Grunde nicht das tut, was Sie erwartet hatten, und daher nicht etwas ist, das Sie tatsächlich auf diese Weise verwenden würden) - Ich hoffe, der Code funktioniert besser als meine Kennzeichnung als Code, und Hey, sieh dir diese letzte Zeile an:
// 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()); } }
quelle
Ich mag die Antwort vom 30. mit
Iterators
Guave. Von einigen Frameworks erhalte ich jedoch null anstelle eines leeren Arrays und kannIterators.forArray(array)
damit nicht gut umgehen. Also habe ich mir diese Hilfsmethode ausgedacht, mit der Sie aufrufen könnenIterator<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; } }; }
quelle