Arrays.asList() не работает должным образом?

У меня есть 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[]?

Ответ 1

Как насчет этого?

Integer[] ints = new Integer[] {1,2,3,4,5};
List<Integer> list = Arrays.asList(ints);

Ответ 2

В Java нет такой вещи, как List<int> - дженерики не поддерживают примитивы.

Автобоксирование происходит только для одного элемента, а не для массивов примитивов.

Что касается того, как исправить это - существуют различные библиотеки с кучами методов для подобных действий. Нет никакого способа обойти это, и я не думаю, что там было что-то, что облегчило бы в JDK. Некоторые будут обертывать примитивный массив в списке типа обертки (так что бокс происходит при доступе), другие будут проходить через исходный массив, чтобы создать независимую копию, бокс по ходу. Убедитесь, что вы знаете, что используете.

(EDIT: Я предполагал, что начальная точка int[] не подлежит обсуждению. Если вы можете начать с Integer[], тогда вам будет далеко:)

Просто для одного примера вспомогательной библиотеки и немного подключите Guava, com.google.common.primitive.Ints.asList.

Ответ 3

Поскольку массивы java являются объектами, а Arrays.asList() рассматривает ваш массив int как одиночный аргумент в списке varargs.

Ответ 4

Войдите в 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 внутри.

Ответ 5

Проблема не в Arrays.asList(). Проблема в том, что вы ожидаете, что autoboxing будет работать с массивом - и это не так. В первом случае компилятор автоматически создает отдельные блоки, прежде чем он будет смотреть на то, для чего они используются. Во втором случае вы сначала помещаете их в массив int (нет необходимости в autoboxing), а затем передайте это на Arrays.asList() (возможно, не автобоксирование).

Ответ 6

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 IntStream

В Java 8 вы можете использовать метод Arrays.stream(int[]), чтобы превратить массив int в Stream. В зависимости от вашего варианта использования вы можете напрямую использовать поток, например, делать что-либо с каждым элементом с помощью forEach(IntConsumer). В этом случае это решение выполняется очень быстро и не требует никакого бокса или распаковки вообще и не создает никакой копии базового массива.

Альтернативно, если вам действительно нужен List<Integer>, вы можете использовать stream.boxed().collect(Collectors.toList()) как здесь. Недостатком этого подхода является то, что он полностью помещает каждый элемент в список, что может увеличить его объем памяти почти на порядок, он создает новый Object[] для хранения всех элементов в коробке. Если вы впоследствии используете список в большой степени и нуждаетесь в Integer объектах, а не в int s, это может окупиться, но это то, о чем нужно знать.

Ответ 7

Если вы передадите int[] в Arrays.asList(), созданный список будет List<int[]>, который не является vaild в java, а не правильный List<Integer>.

Я думаю, вы ожидаете, что Arrays.asList() будет автоматически блокировать ваши ints, что, как вы видели, не будет.

Ответ 8

Невозможно преобразовать 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;
}

Ответ 9

Почему здесь не работает автобоксинг; т.е. 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 -..... при условии, что вы не используете == чтобы проверить, равны ли значения автобокса.

Ответ 10

На мой взгляд, Arrays - это удобная утилита, поэтому она должна быть удобной. Когда кто-то намеревается получить список из одного элемента, когда он вызывает Arrays.aslist(int [])? Массивы способны проверять int [], как демонстрирует Arrays.stream(int []), так почему бы не сделать List или сделать то, что делает Guava? @BeeOnRope объяснил варианты хорошо. Опять же, это удобная утилита. Если вы хотите настроить производительность или убедиться, что у вас есть целое число, а не целое число, вы должны это кодировать.