Как упрощенная инструкция оператора работает для массивов и как получить iterator для массива?

Учитывая следующий fragment кода:

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

У меня есть следующие вопросы:

  1. Как работает цикл выше для каждого цикла?
  2. Как получить iterator для массива в Java?
  3. Является ли массив преобразованным в список для получения iteratorа?

Если вы хотите использовать Iterator над массивом, вы можете использовать одну из прямых реализаций вместо того, чтобы обернуть массив в List . Например:

Коллекции коллекционеров Apache ArrayIterator

Или, если вы хотите использовать дженерики:

com.Ostermiller.util.ArrayIterator

Обратите внимание: если вы хотите иметь Iterator над примитивными типами, вы не можете, потому что примитивный тип не может быть общим параметром. Например, если вы хотите Iterator , вместо этого вы должны использовать Iterator , что приведет к большому количеству автобоксинга и -unboxing, если это поддерживается int[] .

Нет, нет конверсии. JVM просто выполняет итерацию по массиву с использованием индекса в фоновом режиме.

Цитата из Effective Java 2nd Ed., Item 46:

Обратите внимание, что для использования цикла for-each нет ограничений производительности даже для массивов. Фактически, он может предложить небольшое преимущество в производительности по сравнению с обычным циклом в некоторых случаях, поскольку он вычисляет предел индекса массива только один раз.

Таким образом, вы не можете получить Iterator для массива (если, конечно, путем его преобразования в List сначала).

Arrays.asList (обр) .iterator ();

Или напишите свой собственный, реализуя интерфейс ListIterator ..

Коллекция Guava Librarie s обеспечивает такую ​​функцию:

 Iterator it = Iterators.forArray(array); 

Нужно предпочесть Guava над Apache Collection (который, кажется, заброшен).

В 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(); } } 

Строго говоря, вы не можете получить iterator примитивного массива, потому что Iterator.next () может возвращать Object только. Но благодаря магии автобоксинга вы можете получить iterator, используя метод Arrays.asList () .

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

Вышеупомянутый ответ неверен, вы не можете использовать Arrays.asList() в примитивном массиве, он вернет List . Ints.asList() этого используйте Ints.asList() Guava .

Вы не можете напрямую получить iterator для массива.

Но вы можете использовать List, поддерживаемый вашим массивом, и получить ierator в этом списке. Для этого ваш массив должен быть массивом Integer (вместо массива int):

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

Примечание: это только теория. Вы можете получить iterator, как это, но я препятствую вам это сделать. Выступления не очень хороши по сравнению с прямой итерацией в массиве с расширением для синтаксиса.

Примечание 2: Конструкция списка с этим методом не поддерживает все методы (поскольку список поддерживается массивом с фиксированным размером). Например, метод «удалить» вашего iteratorа приведет к исключению.

Как работает цикл выше для каждого цикла?

Как и многие другие функции массива, JSL явно упоминает массивы и дает им магические свойства. JLS 7 14.14.2 :

 EnhancedForStatement: for ( FormalParameter : Expression ) Statement 

[…]

Если тип выражения является подтипом Iterable , тогда перевод выглядит следующим образом

[…]

В противном случае выражение обязательно имеет тип массива, T[] . [[MAGIC! ]]

Пусть L1 ... Lm – (возможно, пустая) последовательность меток, непосредственно предшествующая расширению для оператора.

Расширение для утверждения эквивалентно базовому для формулировки формы:

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

#a и #i - это автоматически сгенерированные идентификаторы, отличные от любых других идентификаторов (автоматически сгенерированных или других), которые находятся в области действия в точке, где происходит расширение для оператора.

Является ли массив преобразованным в список для получения iteratorа?

Давайте 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 : создать массив
  • 22 : подготовьтесь к циклу for. В точке 22 сохраните целое число 0 из стека в локальное положение 4 . Это переменная цикла.
  • 24 - 47 : петля. Переменная цикла извлекается на 31 и увеличивается на 44 . Когда он равен длине массива, которая хранится в локальной переменной 3 при проверке на 27 , цикл заканчивается.

Вывод : это то же самое, что и для явного цикла цикла с индексной переменной, при этом не задействованы iteratorы.

Для (2), Guava предоставляет именно то, что вы хотите как Int.asList () . Существует эквивалент для каждого примитивного типа в ассоциированном classе, например, boolean для boolean и т. Д.

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

Я немного опаздываю на игру, но я заметил некоторые ключевые моменты, которые были упущены, особенно в отношении Java 8 и эффективности Arrays.asList .

1. Как работает цикл for-each?

Как отметил Сиро Сантилли , есть удобная утилита для изучения байт-кода, который поставляется с JDK: javap . Используя это, мы можем определить, что следующие два fragmentа кода производят одинаковый байт-код с языка 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); } } 

2. Как получить iterator для массива в Java?

Хотя это не работает для примитивов, следует отметить, что преобразование массива в список с помощью массива Arrays.asList не оказывает существенного влияния на производительность. Влияние на память и производительность почти неизмеримо.

Arrays.asList не используется обычная реализация List, которая легко доступна как class. Он использует java.util.Arrays.ArrayList , который не совпадает с java.util.ArrayList . Это очень тонкая shell вокруг массива и не может быть изменена. Посмотрев исходный код java.util.Arrays.ArrayList , мы видим, что он предназначен для функционального эквивалента массиву. Накладных расходов почти нет. Обратите внимание, что я пропустил все, кроме самого релевантного кода, и добавил свои собственные комментарии.

 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; } } } 

Итератор находится в java.util.AbstractList.Itr . Что касается iteratorов, это очень просто; он просто вызывает get() пока не будет достигнут size() , подобно тому, как это делает инструкция для цикла. Это самая простая и обычно самая эффективная реализация Iterator для массива.

Опять же, Arrays.asList не создает java.util.ArrayList . Он намного более легкий и подходит для получения iteratorа с незначительными накладными расходами.

Примитивные массивы

Как отмечали другие, Arrays.asList нельзя использовать на примитивных массивах. Java 8 представляет несколько новых технологий для обработки коллекций данных, некоторые из которых могут быть использованы для извлечения простых и относительно эффективных iteratorов из массивов. Обратите внимание: если вы используете generics, у вас всегда будет проблема с бокс-распаковкой: вам нужно будет преобразовать из int в Integer, а затем обратно в int. В то время как бокс / распаковка обычно незначительна, в этом случае она имеет влияние производительности O (1) и может привести к проблемам с очень большими массивами или компьютерами с очень ограниченными ресурсами (т. Е. SoC ).

Мой личный фаворит для любого вида операций по литью / боксу в Java 8 - это новый stream API. Например:

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

API streamов также предлагает конструкции для предотвращения проблемы бокса, в первую очередь, но это требует отказа от iteratorов в пользу streamов. Существуют специальные типы streamов для 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 через бокс вместе с методами, чтобы избежать бокса. PrimitiveIterator имеет три встроенных интерфейса, которые расширяют его: OfInt, OfLong и OfDouble. Все три будут помечены, если вызывается next() но также могут возвращать примитивы с помощью таких методов, как nextInt() . Более next() код, предназначенный для Java 8, должен избегать использования next() если только бокс абсолютно необходим.

 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); } 

Если вы еще не на Java 8, к сожалению, ваш самый простой вариант намного менее краток и почти наверняка будет включать бокс:

 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++]; } }; 

Или если вы хотите создать что-то более многоразовое:

 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++]; } } 

Вы можете обойти проблему бокса, добавив свои собственные методы для получения примитивов, но это будет работать только с вашим собственным внутренним кодом.

3. Является ли массив преобразованным в список для получения iteratorа?

Нет. Однако это не означает, что обертывание в списке приведет к снижению производительности, если вы используете что-то легкое, например, Arrays.asList .

Я недавний студент, но я ВЕРЮ пример оригинала, когда int [] выполняет итерацию по массиву примитивов, но не используя объект Iterator. Он просто имеет тот же (похожий) синтаксис с различным содержимым,

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

Arrays.asList () APPARENTLY просто применяет методы List к массиву объектов, который ему задан, но для любого другого типа объекта, включая примитивный массив, iterator (). Next () APPARENTLY просто передает вам ссылку на исходный объект, обрабатывая это как список с одним элементом. Можем ли мы увидеть исходный код для этого? Вы предпочли бы исключение? Неважно. Я думаю (это УГАДАЙ), что это как (или это) коллекция Singleton. Итак, здесь asList () не имеет отношения к случаю с массивом примитивов, но запутанным. Я НЕ ЗНАЮ, что я прав, но я написал программу, которая говорит, что я есть.

Таким образом, этот пример (где в принципе asList () не делает то, что вы считали, и поэтому не является тем, что вы на самом деле использовали таким образом). Надеюсь, что код работает лучше, чем мой код маркировки, эй, посмотри на эту последнюю строчку:

 // 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()); } } 

Мне нравится ответ с 30-го, используя Iterators из Гуавы. Однако из некоторых фреймворков я получаю null вместо пустого массива, а Iterators.forArray(array) не справляется с этим. Поэтому я придумал этот вспомогательный метод, который вы можете вызвать с помощью 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; } }; } 
  • Передача массива по ссылке
  • Каков правильный способ инициализации массива с фиксированной длиной?
  • Как получить доступ к многомерному массиву и управлять им с помощью имен / путей ключа?
  • Как фильтровать массив объектов на основе значений во внутреннем массиве с jq?
  • байтовый массив в короткий массив и обратно в java
  • Как лучше всего обрабатывать динамические многомерные массивы в C / C ++?
  • Преобразовать ArrayList в String array
  • Структура массивов по сравнению с массивом структур в CUDA
  • Присоединить элементы массива?
  • Получает массив байтов из ByteBuffer в java
  • Java-создание байтового массива, размер которого представлен длинным
  • Давайте будем гением компьютера.