Ответ 1
Как насчет этого?
Integer[] ints = new Integer[] {1,2,3,4,5};
List<Integer> list = Arrays.asList(ints);
У меня есть float [], и я хотел бы получить список с теми же элементами. Я мог сделать уродливую вещь, добавляя их один за другим, но я хотел использовать метод Arrays.asList. Однако есть проблема. Это работает:
List<Integer> list = Arrays.asList(1,2,3,4,5);
Но это не так.
int[] ints = new int[] {1,2,3,4,5};
List<Integer> list = Arrays.asList(ints);
Метод asList принимает параметр varargs, который для расширений моих знаний является "сокращением" для массива.
Вопросы:
Почему вторая часть кода возвращает List<int[]>
но не List<int>
.
Есть ли способ исправить это?
Почему здесь не работает автобоксинг; т.е. int[]
в Integer[]
?
Как насчет этого?
Integer[] ints = new Integer[] {1,2,3,4,5};
List<Integer> list = Arrays.asList(ints);
В Java нет такой вещи, как List<int>
- дженерики не поддерживают примитивы.
Автобоксирование происходит только для одного элемента, а не для массивов примитивов.
Что касается того, как исправить это - существуют различные библиотеки с кучами методов для подобных действий. Нет никакого способа обойти это, и я не думаю, что там было что-то, что облегчило бы в JDK. Некоторые будут обертывать примитивный массив в списке типа обертки (так что бокс происходит при доступе), другие будут проходить через исходный массив, чтобы создать независимую копию, бокс по ходу. Убедитесь, что вы знаете, что используете.
(EDIT: Я предполагал, что начальная точка int[]
не подлежит обсуждению. Если вы можете начать с Integer[]
, тогда вам будет далеко:)
Просто для одного примера вспомогательной библиотеки и немного подключите Guava, com.google.common.primitive.Ints.asList
.
Поскольку массивы java являются объектами, а Arrays.asList()
рассматривает ваш массив int как одиночный аргумент в списке varargs.
Войдите в Java 8, и вы можете сделать следующее для сбора в массиве в штучной упаковке:
Integer[] boxedInts = IntStream.of(ints).boxed().toArray(Integer[]::new);
Или это, чтобы собрать в коробке Список
List<Integer> boxedInts = IntStream.of(ints).boxed().collect(Collectors.toList());
Однако это работает только для int[]
, long[]
и double[]
. Это не будет работать для byte[]
.
Обратите внимание, что Arrays.stream(ints)
и IntStream.of(ints)
эквивалентны. Поэтому ранее два примера можно также переписать как:
Integer[] boxedIntArray = Arrays.stream(ints).boxed().toArray(Integer[]::new);
List<Integer> boxedIntList = Arrays.stream(ints).boxed().collect(Collectors.toList());
Эта последняя форма может быть одобрена, поскольку она опускает примитивный специфический подтип Stream
. Однако внутренне это все еще куча перегруженных, которые в этом случае все же создают IntStream
внутри.
Проблема не в Arrays.asList()
. Проблема в том, что вы ожидаете, что autoboxing будет работать с массивом - и это не так. В первом случае компилятор автоматически создает отдельные блоки, прежде чем он будет смотреть на то, для чего они используются. Во втором случае вы сначала помещаете их в массив int (нет необходимости в autoboxing), а затем передайте это на Arrays.asList()
(возможно, не автобоксирование).
Arrays.asList(T... a)
эффективно принимает T[]
, который будет соответствовать любому массиву истинных объектов (подклассы Object
) в качестве массива. Единственное, что не соответствует этому, - это массив примитивов, поскольку примитивные типы не происходят из Object
. Таким образом, int[]
не является Object[]
.
Что происходит тогда, так это то, что механизм varags срабатывает и обрабатывает его, как если бы вы передали один объект, и создает один массив элементов этого типа. Итак, вы передаете int[][]
(здесь T
is int[]
) и в итоге получим 1-элемент List<int[]>
, который вам не нужен.
У вас все еще есть несколько хороших вариантов:
Int.asList(int[])
АдаптерЕсли ваш проект уже использует guava, он так же прост, как и с помощью адаптера. Guava предоставляет: Int.asList(). Существует аналогичный адаптер для каждого примитивного типа в ассоциированном классе, например, Booleans
для boolean
и т.д.
int foo[] = {1,2,3,4,5};
Iterable<Integer> fooBar = Ints.asList(foo);
for(Integer i : fooBar) {
System.out.println(i);
}
Преимущество этого подхода состоит в том, что он создает тонкую оболочку вокруг существующего массива, поэтому создание оболочки является постоянным временем (не зависит от размера массива), а требуемое хранилище - это лишь небольшая постоянное количество (менее 100 байт) в дополнение к базовому целочисленному массиву.
Недостатком является то, что для доступа к каждому элементу требуется операция бокса базового int
, а для установки требуется распаковка. Это может привести к значительному распределению временной области при доступе к списку в значительной степени. Если вы в среднем получаете доступ к каждому объекту много раз, может быть лучше использовать реализацию, которая однажды помещает объекты и сохраняет их как Integer
. Ниже приведенное ниже решение.
В Java 8 вы можете использовать метод Arrays.stream(int[])
, чтобы превратить массив int
в Stream
. В зависимости от вашего варианта использования вы можете напрямую использовать поток, например, делать что-либо с каждым элементом с помощью forEach(IntConsumer)
. В этом случае это решение выполняется очень быстро и не требует никакого бокса или распаковки вообще и не создает никакой копии базового массива.
Альтернативно, если вам действительно нужен List<Integer>
, вы можете использовать stream.boxed().collect(Collectors.toList())
как здесь. Недостатком этого подхода является то, что он полностью помещает каждый элемент в список, что может увеличить его объем памяти почти на порядок, он создает новый Object[]
для хранения всех элементов в коробке. Если вы впоследствии используете список в большой степени и нуждаетесь в Integer
объектах, а не в int
s, это может окупиться, но это то, о чем нужно знать.
Если вы передадите int[]
в Arrays.asList()
, созданный список будет List<int[]>
, который не является vaild в java, а не правильный List<Integer>
.
Я думаю, вы ожидаете, что Arrays.asList()
будет автоматически блокировать ваши ints, что, как вы видели, не будет.
Невозможно преобразовать int[]
в Integer[]
, вам нужно скопировать значения
int[] tab = new int[]{1, 2, 3, 4, 5};
List<Integer> list = ArraysHelper.asList(tab);
public static List<Integer> asList(int[] a) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < a.length && list.add(a[i]); i++);
return list;
}
Почему здесь не работает автобоксинг; т.е. int [] в Integer []?
Хотя autoboxing преобразует int
в Integer
, он не преобразует int[]
в Integer[]
.
Почему бы и нет?
Простой (но неудовлетворительный) ответ - это то, о чем говорит JLS. (Вы можете проверить это, если хотите.)
Настоящий ответ является основополагающим для того, что делает autoboxing и почему оно безопасно.
Когда вы используете autobox 1
в вашем коде, вы получаете тот же объект Integer
. Это неверно для всех значений int
(из-за ограниченного размера кеша автобоксинга Integer
), но если вы используете equals
для сравнения объектов Integer
вы получаете "правильный" ответ.
В основном N == N
всегда истинно, и new Integer(N).equals(new Integer(N))
всегда верен. Кроме того, эти две вещи остаются верными... если вы придерживаетесь кода Pure Java.
Теперь рассмотрим следующее:
int[] x = new int[]{1};
int[] y = new int[]{1};
Являются ли они равными? Нет! x == y
является ложным, а x.equals(y)
неверно! Но почему? Так как:
y[0] = 2;
Другими словами, два массива с одинаковым типом, размером и содержимым всегда различаются, потому что массивы Java изменяемы.
"Обещание" автобоксинга состоит в том, что это нормально, потому что результаты неразличимы 1. Но, поскольку все массивы фундаментально различимы из-за определения equals
для массивов и изменчивости массива. Итак, если разрешено автобоксирование массивов примитивных типов, это подорвет "обещание".
1 -..... при условии, что вы не используете ==
чтобы проверить, равны ли значения автобокса.
На мой взгляд, Arrays - это удобная утилита, поэтому она должна быть удобной. Когда кто-то намеревается получить список из одного элемента, когда он вызывает Arrays.aslist(int [])? Массивы способны проверять int [], как демонстрирует Arrays.stream(int []), так почему бы не сделать List или сделать то, что делает Guava? @BeeOnRope объяснил варианты хорошо. Опять же, это удобная утилита. Если вы хотите настроить производительность или убедиться, что у вас есть целое число, а не целое число, вы должны это кодировать.