Как упрощенная инструкция оператора работает для массивов и как получить iterator для массива?
Учитывая следующий fragment кода:
int[] arr = {1, 2, 3}; for (int i : arr) System.out.println(i);
У меня есть следующие вопросы:
- Как работает цикл выше для каждого цикла?
- Как получить iterator для массива в Java?
- Является ли массив преобразованным в список для получения iteratorа?
- Что произойдет, если я определяю массив 0-размера в C / C ++?
- Удалить поле из всех элементов массива в mongodb
- Как хранятся 3D-массивы в C?
- Как создать правильный JSONArray в Java с помощью JSONObject
- Генерирование Java и инициализация массива
- Удаление элементов в массиве, если элемент является определенным значением VBA
- В задании A (:) = B количество элементов в A и B должно быть одинаковым
- Передача 2D-массива функции C ++
- c ++ array - выражение должно иметь постоянное значение
- Удалить элемент из массива
- Реверсирование массива в Java
- Можно ли использовать константную переменную для объявления размера массива в C?
- Как удалить определенное значение из массива с помощью jQuery
Если вы хотите использовать 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
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; } }; }