Как работает оператор enhanced for для массивов и как получить итератор для массива?
дан следующий фрагмент кода:
int[] arr = {1, 2, 3};
for (int i : arr)
System.out.println(i);
У меня есть следующие вопросы:
- как работает выше для каждого цикла?
- как получить итератор для массива в Java?
- массив преобразуется в список, чтобы получить итератор?
13 ответов:
если вы хотите
Iteratorнад массивом вы можете использовать одну из прямых реализаций вместо того, чтобы обернуть массив вList. Например:Apache Commons Collections
ArrayIteratorили, этот, если вы хотите использовать дженерики:
com.Ostermiller.util.ArrayIteratorобратите внимание, что если вы хотите иметь
Iteratorнад примитивными типами вы не можете, потому что примитивный тип не может быть универсальным параметром. Например, если вы хотитеIterator<int>, вы должны использоватьIterator<Integer>вместо этого, что приведет к большому количеству автобоксов и распаковки, если это подкрепленоint[].
нет, нет преобразования. JVM просто перебирает массив, используя индекс в фоновом режиме.
цитата из эффективного Java 2-е изд. Пункт 46:
обратите внимание, что нет никакого штрафа за использование цикл for-each, даже для массивов. На самом деле, он может предложить небольшое преимущество в производительности над обычным циклом for В некоторых обстоятельствах, поскольку он вычисляет предел индекс массива только один раз.
Так что вы не можете получить
Iteratorдля массива (если, конечно, путем преобразования его вListпервая).
Google Гуава Библиотекаколлекция s предоставляет такую функцию:
Iterator<String> it = Iterators.forArray(array);следует предпочесть Guava над коллекцией Apache (которая, похоже, заброшена).
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(); } }
строго говоря, вы не можете получить итератор примитивного массива, потому что итератор.далее () может возвращать только объект. Но благодаря магии автобоксинга, вы можете получить итератор с помощью массивы.asList() метод.Iterator<Integer> it = Arrays.asList(arr).iterator();приведенный выше ответ неверен, вы не можете использовать
Arrays.asList()на примитивном массиве он вернет aList<int[]>. Используйте гуавы ' s .
вы не можете напрямую получить итератор для массива.
но вы можете использовать список, поддержанный вашим массивом, и получить ierator в этом списке. Для этого Ваш массив должен быть целое массив (вместо массива int):
Integer[] arr={1,2,3}; List<Integer> arrAsList = Arrays.asList(arr); Iterator<Integer> iter = arrAsList.iterator();Примечание: это только теория. Вы можете получить итератор, как это, но я отговариваю вас делать это. Производительность не очень хороша по сравнению с прямой итерацией по массиву с "расширенным синтаксисом".
примечание 2: Список конструкция с этим методом не поддерживает все методы (так как список поддерживается массивом, который имеет фиксированный размер). Например, метод "remove" вашего итератора приведет к исключению.
как работает выше для каждого цикла?
как и многие другие функции массива, JSL явно упоминает массивы и дает им магические свойства. JLS 7 14.14.2:
EnhancedForStatement: for ( FormalParameter : Expression ) Statement[...]
если тип выражения является подтипом
Iterable, то перевод выглядит следующим образом[...]
в противном случае выражение обязательно имеет тип массива,
T[]. [- "Магия! ]]пусть
L1 ... Lmбыть (возможно, пустой) последовательностью меток, непосредственно предшествующих расширенной инструкции for.расширенный оператор for эквивалентен базовому оператору for вида:
T[] #a = Expression; L1: L2: ... Lm: for (int #i = 0; #i < #a.length; #i++) { VariableModifiersopt TargetType Identifier = #a[#i]; Statement }
#aи#iавтоматически генерируемые идентификаторы, которые отличаются от любых других идентификаторов (автоматически генерируемых или иным образом), которые находятся в области действия в точке, где оператор enhanced for происходит.массив преобразуется в список, чтобы получить итератор?
давайте
javapэто:public class ArrayForLoop { public static void main(String[] args) { int[] arr = {1, 2, 3}; for (int i : arr) System.out.println(i); } }затем:
javac ArrayForLoop.java javap -v ArrayForLoop
mainметод с небольшим количеством редактирования, чтобы сделать его легче читать: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поломки:
0до14: создать массив15до22: подготовка к циклу for. В 22, хранить целое число0от стек в локальное положение4. Это переменная цикла.24до47: петля. Переменная цикла извлекается по адресу31, и увеличивается на44. Когда он равен длине массива, который хранится в локальной переменной 3 при проверке на27, то цикл заканчивается.вывод: это то же самое, что делать явный цикл for с переменной индекса, без итераторов.
для (2), гуава обеспечивает именно то, что вы хотите, как Int.asList(). Существует эквивалент для каждого примитивного типа в связанном классе, например,
Booleansнаbooleanи т. д.int[] arr={1,2,3}; for(Integer i : Ints.asList(arr)) { System.out.println(i); }
я немного опоздал в игру, но я заметил некоторые ключевые моменты, которые были опущены, особенно в отношении Java 8 и эффективности
Arrays.asList.1. Как работает цикл for-each?
как Ciro Santilli件件 法轮功 包卓轩 указал, что есть удобная утилита для изучения байт-кода, который поставляется с JDK:
javap. Используя это, мы можем определить, что следующие два фрагмента кода производят идентичный байт-код на Java 8u74:для каждого цикла:
int[] arr = {1, 2, 3}; for (int n : arr) { System.out.println(n); }цикл for:
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. Как получить итератор для массива в Java?
пока это не работает для примитивов, следует отметить, что преобразование массива в список
Arrays.asListне влияет на производительность в любом существенным образом. Влияние на память и производительность почти неизмеримо.
Arrays.asListне использует обычную реализацию списка, которая легко работает как класс. Он используетjava.util.Arrays.ArrayList, который не совпадает сjava.util.ArrayList. Это очень тонкая оболочка вокруг массива и не может быть изменен. Глядя на исходный кодjava.util.Arrays.ArrayList, мы видим, что он разработан, чтобы быть функционально эквивалентным массиву. Почти нет накладных расходов. Обратите внимание, что я опустил все, кроме наиболее релевантного кода и добавил свои собственные комментарии.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; } } }итератор находится в
java.util.AbstractList.Itr. Что касается итераторов, это очень просто; он просто вызываетget()доsize()достигается, так же, как руководство для цикла будет делать. Это самая простая и, как правило, наиболее эффективная реализацияIteratorдля массива.опять
Arrays.asListне создаетjava.util.ArrayList. Это гораздо более легкий и подходит для получения итератора с незначительными накладными расходами.примитивные массивы
как уже отмечалось,
Arrays.asListне может использоваться на примитивных массивах. Java 8 представляет несколько новых технологий для работа с коллекциями данных, некоторые из которых могут быть использованы для извлечения простых и относительно эффективных итераторов из массивов. Обратите внимание, что если вы используете дженерики, у вас всегда будет проблема бокса-распаковки: вам нужно будет преобразовать из int в Integer, а затем обратно в int. Хотя бокс / распаковка обычно незначительна, в этом случае она оказывает влияние на производительность O(1) и может привести к проблемам с очень большими массивами или на компьютерах с очень ограниченными ресурсами (т. е., SoC).мой личный фаворит для любого вида операции литья массива / бокса в Java 8-это новый stream API. Например:
int[] arr = {1, 2, 3}; Iterator<Integer> iterator = Arrays.stream(arr).mapToObj(Integer::valueOf).iterator();API потоков также предлагает конструкции для избежания проблемы бокса в первую очередь, но это требует отказа от итераторов в пользу потоков. Существуют выделенные типы потоков для int, long и double (IntStream, LongStream и DoubleStream соответственно).
int[] arr = {1, 2, 3}; IntStream stream = Arrays.stream(arr); stream.forEach(System.out::println);интересно, В Java 8 также добавляет
java.util.PrimitiveIterator. Это обеспечивает лучшее из обоих миров: совместимость сIterator<T>через бокс вместе с методами, чтобы избежать бокса. PrimitiveIterator имеет три встроенных интерфейса, которые расширяют его: OfInt, OfLong и OfDouble. Все три будут боксировать, еслиnext()вызывается, но также может возвращать примитивы с помощью таких методов, какnextInt(). Более новый код, предназначенный для Java 8, должен избегать использованияnext()если бокс не является абсолютно необходимым.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); }если вы еще не на Java 8, к сожалению, Ваш самый простой вариант намного менее лаконичен и почти наверняка будет включать в себя бокс:
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++]; } };или если вы хотите создать что-то более многоразовый:
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++]; } }вы можете обойти проблему бокса здесь, добавив свои собственные методы получения примитивов, но это будет работать только с вашим собственным внутренним кодом.
3. Массив преобразуется в список, чтобы получить итератор?
нет, это не так. Однако, это не значит, что обертывание его в список даст вам худшую производительность, если вы используете что-то легкое, например
Arrays.asList.
Я недавний студент, но я считаю, что исходный пример с int[] повторяет массив примитивов, но не с помощью объекта итератора. Он просто имеет тот же (подобный) синтаксис с различным содержанием,
for (primitive_type : array) { } for (object_type : iterableObject) { }массивы.asList () по-видимому, просто применяет методы списка к массиву объектов, который он задан, но для любого другого типа объекта, включая примитивный массив, iterator().далее () видимо просто передает вам ссылку на исходный объект, рассматривая его как список с одним элементом. Можем ли мы увидеть исходный код для этого? Разве вы не предпочли бы исключение? Не берите в голову. Я предполагаю (это предположение), что это похоже (или это) на Одноэлементную коллекцию. Поэтому здесь asList () не имеет отношения к случаю с массивом примитивов, но запутывает. Я не знаю, что я прав, но я написал программу, которая говорит, что я прав.
таким образом, этот пример (где в основном asList() не делает то, что вы думали, и поэтому это не то, что вы на самом деле используете таким образом) - I надеюсь, что код работает лучше, чем моя маркировка как код, и, Эй, посмотрите на эту последнюю строку:
// 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()); } }
мне нравится ответ от 30thh с помощью
Iteratorsиз гуавы. Однако из некоторых фреймворков я получаю null вместо пустого массива, иIterators.forArray(array)не справляется с этим хорошо. Поэтому я придумал этот вспомогательный метод, который можно вызвать с помощью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; } }; }
Comments