Разница между Arrays.asList (массив) и новым ArrayList <Integer> (Arrays.asList (массив))

119

В чем разница между

1.List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));  //copy
2.List<Integer> list2 = Arrays.asList(ia);

где ia- массив целых чисел.

Я узнал, что некоторые операции не допускаются list2. почему это так? как он хранится в памяти (ссылки / копия)?

Когда я перетасовываю списки, list1это не влияет на исходный массив, но влияет list2. Но все list2же несколько сбивает с толку.

Чем ArrayListповышение до списка отличается от создания новогоArrayList

list1 differs from (1)
ArrayList<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));
Dineshkumar
источник
2
Предлагаю вам присмотреться к варианту Google Guava . Lists.newArrayList(ia)делает независимую копию, как и в первом варианте. Это просто более общий характер, и на него лучше смотреть.
qben

Ответы:

230
  1. Сначала давайте посмотрим, что это делает:

    Arrays.asList(ia)

    Он принимает массив iaи создает оболочку, которая реализует List<Integer>, что делает исходный массив доступным в виде списка. Ничего не копируется и все, создается только один объект-оболочка. Операции с оболочкой списка распространяются на исходный массив. Это означает, что если вы перетасовываете оболочку списка, перетасовывается и исходный массив, если вы перезаписываете элемент, он перезаписывается в исходном массиве и т. Д. Конечно, некоторые Listоперации с оболочкой не разрешены, такие как добавление или удаляя элементы из списка, вы можете только читать или перезаписывать элементы.

    Обратите внимание, что оболочка списка не расширяется ArrayList- это объект другого типа. ArrayLists имеют свой собственный внутренний массив, в котором они хранят свои элементы, и могут изменять размер внутренних массивов и т. д. Обертка не имеет собственного внутреннего массива, она только передает операции в массив, переданный ей.

  2. С другой стороны, если вы впоследствии создадите новый массив как

    new ArrayList<Integer>(Arrays.asList(ia))

    затем вы создаете новую ArrayList, которая является полной независимой копией оригинала. Хотя здесь вы также создаете оболочку using Arrays.asList, она используется только во время создания нового ArrayListи после этого собирается сборщиком мусора. Структура этого нового ArrayListполностью независима от исходного массива. Он содержит те же элементы (и исходный массив, и эта новая ArrayListссылка на одни и те же целые числа в памяти), но создает новый внутренний массив, содержащий ссылки. Поэтому, когда вы перетасовываете его, добавляете, удаляете элементы и т. Д., Исходный массив не изменяется.

Петр Пудлак
источник
9
@Dineshkumar Обертка - это шаблон проектирования, который переводит один интерфейс класса в другой интерфейс. См. Статью о шаблоне оболочки . | Куда нужно апкастировать? Я бы предложил использовать List<Integer>для ваших типов переменных (или аргументов метода и т. Д.). Это делает ваш код более универсальным, и вы можете легко переключиться на другую Listреализацию по мере необходимости без необходимости переписывать много кода.
Петр Пудлак
Это хорошее объяснение. Расширяя этот вопрос, метод Arrays.asList () также принимает varargs. Если я передам определенные значения, скажем, дважды: Arrays.asList (1,3,5), вернет ли он тот же список?
sbsatter
27

Это ж потому, что ArrayListрезультат Arrays.asList()не того типа java.util.ArrayList. Arrays.asList()создает ArrayListтип, java.util.Arrays$ArrayListкоторый не расширяется, java.util.ArrayListа только расширяетсяjava.util.AbstractList

Крис
источник
9
List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));  //copy

В данном случае list1тип ArrayList.

List<Integer> list2 = Arrays.asList(ia);

Здесь список возвращается как Listпредставление, то есть в нем есть только методы, прикрепленные к этому интерфейсу. Следовательно, почему некоторые методы не разрешены list2.

ArrayList<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));

Здесь вы СОЗДАЕТЕ новый ArrayList. Вы просто передаете ему значение в конструкторе. Это не пример кастинга. В кастинге это могло бы выглядеть примерно так:

ArrayList list1 = (ArrayList)Arrays.asList(ia);
Кристофер
источник
4

Я здесь довольно поздно, в любом случае почувствовал, что объяснение со ссылками на документы будет лучше для тех, кто ищет ответ.

  1. java.util.Arrays
  • Это служебный класс с набором статических методов для работы с заданным массивом.
  • asList - один из таких статических методов, который принимает входной массив и возвращает объект java.util.Arrays.ArrayList, который является статическим вложенным классом, который расширяет AbstractList, который inturn реализует интерфейс List.
  • Таким образом, Arrays.asList (inarray) возвращает оболочку List вокруг входного массива, но эта оболочка java.util.Arrays.ArrayList, а не java.util.ArrayList, и ссылается на тот же массив, поэтому добавление дополнительных элементов в массив, завернутый в список, повлияет оригинальный тоже, и мы не можем изменить длину.
  1. java.util.ArrayList
  • ArrayList имеет кучу перегруженных конструкторов

    public ArrayList () - // возвращает ArrayList с емкостью по умолчанию 10

    общедоступный ArrayList (Коллекция c)

    общедоступный ArrayList (int initialCapacity)

  • Поэтому, когда мы передаем возвращаемый объект Arrays.asList, то есть List (AbstractList), второму конструктору выше, он создаст новый динамический массив (этот размер массива увеличивается, когда мы добавляем больше элементов, чем его емкость, а также новые элементы не повлияют на исходный массив ) неглубокое копирование исходного массива ( мелкое копирование означает, что оно копирует только ссылки и не создает новый набор тех же объектов, что и в исходном массиве)

Гаутам Тадигоппула
источник
4
String names[] = new String[]{"Avinash","Amol","John","Peter"};
java.util.List<String> namesList = Arrays.asList(names);

или

String names[] = new String[]{"Avinash","Amol","John","Peter"};
java.util.List<String> temp = Arrays.asList(names);         

Вышеуказанный оператор добавляет оболочку к входному массиву. Таким образом, такие методы, как add и remove, не будут применяться к объекту ссылки на список «namesList».

Если вы попытаетесь добавить элемент в существующий массив / список, вы получите «Исключение в потоке» main «java.lang.UnsupportedOperationException».

Вышеупомянутая операция доступна только для чтения или просмотра.
Мы не можем выполнить операцию добавления или удаления в объекте списка. Но

String names[] = new String[]{"Avinash","Amol","John","Peter"};
java.util.ArrayList<String> list1 = new ArrayList<>(Arrays.asList(names));

или

String names[] = new String[]{"Avinash","Amol","John","Peter"};
java.util.List<String> listObject = Arrays.asList(names);
java.util.ArrayList<String> list1 = new ArrayList<>(listObject);

В приведенном выше заявлении вы создали конкретный экземпляр класса ArrayList и передали список в качестве параметра.

В этом случае метод add & remove будет работать правильно, поскольку оба метода относятся к классу ArrayList, поэтому здесь мы не получим исключения UnSupportedOperationException.
Изменения, внесенные в объект Arraylist (метод добавления или удаления элемента в / из Arraylist), не будут отражены в исходном объекте java.util.List.

String names[] = new String[] {
    "Avinash",
    "Amol",
    "John",
    "Peter"
};

java.util.List < String > listObject = Arrays.asList(names);
java.util.ArrayList < String > list1 = new ArrayList < > (listObject);
for (String string: list1) {
    System.out.print("   " + string);
}
list1.add("Alex"); //Added without any exception
list1.remove("Avinash"); //Added without any exception will not make any changes in original list in this case temp object.


for (String string: list1) {
    System.out.print("   " + string);
}
String existingNames[] = new String[] {
    "Avinash",
    "Amol",
    "John",
    "Peter"
};
java.util.List < String > namesList = Arrays.asList(names);
namesList.add("Bob"); // UnsupportedOperationException occur
namesList.remove("Avinash"); //UnsupportedOperationException
Авинаш Панде
источник
3

Прежде всего, класс Arrays - это служебный класс, который не содержит файлов. служебных методов для работы с массивами (благодаря классу Arrays, в противном случае нам пришлось бы создавать собственные методы для работы с объектами массивов)

asList () метод:

  1. asListМетод является одним из служебных методов Arrayкласса, это статический метод, поэтому мы можем вызывать этот метод по имени его класса (например Arrays.asList(T...a))
  2. Теперь вот поворот, обратите внимание, что этот метод не создает новый ArrayListобъект, он просто возвращает ссылку List на существующий Arrayобъект (так что теперь после использования asListметода Arrayсоздаются две ссылки на существующий объект)
  3. и по этой причине все методы, которые работают с Listобъектом, могут НЕ работать с этим объектом Array, используя Listссылку, например, Arrayразмер s фиксирован по длине, поэтому вы, очевидно, не можете добавлять или удалять элементы из Arrayобъекта, используя эту Listссылку (например, list.add(10)или list.remove(10);иначе он вызовет исключение UnsupportedOperationException)
  4. любое изменение, которое вы делаете, используя ссылку на список, будет отражено при выходе из Arrayобъекта s (поскольку вы работаете с существующим объектом Array, используя ссылку на список)

В первом случае вы создаете новый Arraylistобъект (во втором случае создается только ссылка на существующий объект Array, но не новый ArrayListобъект), поэтому теперь есть два разных объекта: один Arrayобъект, а другой ArrayListобъект, и нет связи между ними (поэтому изменения в одном объекте не будет отражаться / затрагиваться в другом объекте (то есть в случае 2 Arrayи Arraylistэто два разных объекта)

Случай 1:

Integer [] ia = {1,2,3,4};
System.out.println("Array : "+Arrays.toString(ia));
List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));  // new ArrayList object is created , no connection between existing Array Object
list1.add(5);
list1.add(6);
list1.remove(0);
list1.remove(0);
System.out.println("list1 : "+list1);
System.out.println("Array : "+Arrays.toString(ia));

случай 2:

Integer [] ia = {1,2,3,4};
System.out.println("Array : "+Arrays.toString(ia));
List<Integer> list2 = Arrays.asList(ia); // creates only a (new ) List reference to existing Array object (and NOT a new ArrayList Object)
//  list2.add(5); //  it will throw java.lang.UnsupportedOperationException - invalid operation (as Array size is fixed)
list2.set(0,10);  // making changes in existing Array object using List reference - valid 
list2.set(1,11); 
ia[2]=12;     // making changes in existing Array object using Array reference - valid
System.out.println("list2 : "+list2);
System.out.println("Array : "+Arrays.toString(ia));
ирфан
источник
3

Многие люди уже ответили на механические детали, но стоит отметить: это плохой выбор дизайна со стороны Java.

asListМетод Java задокументирован как «Возвращает список фиксированного размера ...». Если вы возьмете его результат и вызовете (скажем) .addметод, он выдаст файл UnsupportedOperationException. Это неинтуитивное поведение! Если метод сообщает, что возвращает a List, стандартное ожидание состоит в том, что он возвращает объект, который поддерживает методы интерфейса List. Разработчику не нужно запоминать, какой из бесчисленных util.Listметодов создает Lists, которые фактически не поддерживают всеList методы.

Если бы они назвали метод asImmutableList, это имело бы смысл. Или, если бы они просто вернули реальный метод List(и скопировали резервный массив), это имело бы смысл. Они решили отдать предпочтение как производительности во время выполнения, так и коротким именам, за счет нарушения как принципа наименьшего неожиданности, так и хорошей OO-практики избегания UnsupportedOperationExceptions.

(Кроме того, дизайнеры могли сделать a interface ImmutableList, чтобы избежать множества UnsupportedOperationExceptions.)

не-только-йети
источник
2

Обратите внимание, что в Java 8 указанное выше 'ia' должно быть Integer [], а не int []. Arrays.asList () массива int возвращает список с одним элементом. При использовании фрагмента кода OP компилятор обнаружит проблему, но некоторые методы (например, Collections.shuffle ()) не смогут сделать то, что вы ожидаете.

Карл Берк
источник
1
Я столкнулся с этой проблемой компилятора, когда сделал ArrayList <Integer> al = new ArrayList <Integer> (Arrays.asList (a)); где a было int []. У меня есть только один элемент, который тоже при печати выглядел мусором. Что это за элемент? И как это случилось? Я столкнулся с этой проблемой в Java 7
Джётсана Нандвани
@JyotsanaNandwani Пожалуйста, проверьте мой ответ: stackoverflow.com/a/54105519/1163607
NINCOMPOOP,
1
package com.copy;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class CopyArray {

    public static void main(String[] args) {
        List<Integer> list1, list2 = null;
        Integer[] intarr = { 3, 4, 2, 1 };
        list1 = new ArrayList<Integer>(Arrays.asList(intarr));
        list1.add(30);
        list2 = Arrays.asList(intarr);
        // list2.add(40); Here, we can't modify the existing list,because it's a wrapper
        System.out.println("List1");
        Iterator<Integer> itr1 = list1.iterator();
        while (itr1.hasNext()) {
            System.out.println(itr1.next());
        }
        System.out.println("List2");
        Iterator<Integer> itr2 = list2.iterator();
        while (itr2.hasNext()) {
            System.out.println(itr2.next());
        }
    }
}
VicXj
источник
1

Arrays.asList()

этот метод возвращает свою собственную реализацию List. Он принимает массив в качестве аргумента и строит методы и атрибуты поверх него, поскольку он не копирует какие-либо данные из массива, а использует исходный массив, это вызывает изменение в исходном массиве при изменении список, возвращаемый Arrays.asList()методом.

с другой стороны.
ArrayList(Arrays.asList()); является конструктором ArrayListкласса, который принимает список в качестве аргумента и возвращает ArrayListнезависимый от списка, т.е. Arrays.asList()в данном случае передается как аргумент. вот почему вы видите эти результаты;

Кунал Сингх Гусейн
источник
0
1.List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));  //copy
2.List<Integer> list2 = Arrays.asList(ia);

В строке 2 Arrays.asList(ia)возвращается Listссылка на объект внутреннего класса, определенный внутри Arrays, который также ArrayListвызывается, но является частным и только расширяется AbstractList. Это означает, что возвращается Arrays.asList(ia)объект класса, отличный от того, что вы получили new ArrayList<Integer>.

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

Взгляните на эту ссылку и посмотрите, что вы можете сделать с частным внутренним классом: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ Arrays.java # Arrays.ArrayList

Строка 1 создает новый ArrayListобъект, копирующий элементы из того, что вы получили из строки 2. Так что вы можете делать все, что захотите, поскольку java.util.ArrayListпредоставляет все эти методы.

zek_w
источник
0

Резюме разницы -

когда список создается без использования нового метода оператора Arrays.asList (), он возвращает Wrapper, что означает

1. вы можете выполнить операцию добавления / обновления.

2. Изменения, сделанные в исходном массиве, также будут отражены в списке и наоборот.

Радж
источник
0

В ответ на некоторые комментарии, задающие вопросы о поведении Arrays.asList () начиная с Java 8:

    int[] arr1 = {1,2,3};
    /* 
       Arrays are objects in Java, internally int[] will be represented by 
       an Integer Array object which when printed on console shall output
       a pattern such as 
       [I@address for 1-dim int array,
       [[I@address for 2-dim int array, 
       [[F@address for 2-dim float array etc. 
   */
    System.out.println(Arrays.asList(arr1)); 

    /* 
       The line below results in Compile time error as Arrays.asList(int[] array)
       returns List<int[]>. The returned list contains only one element 
       and that is the int[] {1,2,3} 
    */
    // List<Integer> list1 = Arrays.asList(arr1);

    /* 
       Arrays.asList(arr1) is  Arrays$ArrayList object whose only element is int[] array
       so the line below prints [[I@...], where [I@... is the array object.
    */
    System.out.println(Arrays.asList(arr1)); 

    /* 
     This prints [I@..., the actual array object stored as single element 
     in the Arrays$ArrayList object. 
    */
    System.out.println(Arrays.asList(arr1).get(0));

    // prints the contents of array [1,2,3]
    System.out.println(Arrays.toString(Arrays.asList(arr1).get(0)));

    Integer[] arr2 = {1,2,3};
    /* 
     Arrays.asList(arr) is  Arrays$ArrayList object which is 
     a wrapper list object containing three elements 1,2,3.
     Technically, it is pointing to the original Integer[] array 
    */
    List<Integer> list2 = Arrays.asList(arr2);

    // prints the contents of list [1,2,3]
    System.out.println(list2);
простофиля
источник