Różnica między Arrays.asList (tablica) a nową ArrayList <Integer> (Arrays.asList (tablica))

119

Jaka jest różnica pomiędzy

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

gdzie iajest tablicą liczb całkowitych.

Dowiedziałem się, że niektóre operacje są niedozwolone list2. dlaczego tak jest jak jest przechowywany w pamięci (odniesienia / kopia)?

Kiedy tasuję listy, list1nie wpływa na oryginalną tablicę, ale list2tak. Ale nadal list2jest nieco zagmatwany.

W jaki sposób ArrayListprzeniesienie do listy różni się od tworzenia nowegoArrayList

list1 differs from (1)
ArrayList<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));
Dineshkumar
źródło
2
Proponuję zajrzeć do opcji Google Guava . Lists.newArrayList(ia)tworzy niezależną kopię, tak jak pierwsza opcja. Jest po prostu bardziej ogólny i lepszy do obejrzenia.
qben

Odpowiedzi:

228
  1. Najpierw zobaczmy, co to robi:

    Arrays.asList(ia)

    Pobiera tablicę iai tworzy opakowanie, które implementuje List<Integer>, dzięki czemu oryginalna tablica jest dostępna jako lista. Nic nie jest kopiowane i tworzony jest tylko jeden obiekt otoki. Operacje na opakowaniu listy są propagowane do oryginalnej tablicy. Oznacza to, że jeśli przetasujesz opakowanie listy, oryginalna tablica również zostanie przetasowana, jeśli nadpiszesz element, zostanie on nadpisany w oryginalnej tablicy itp. Oczywiście niektóre Listoperacje na opakowaniu nie są dozwolone, takie jak dodawanie lub usuwając elementy z listy, możesz tylko czytać lub nadpisywać elementy.

    Zauważ, że opakowanie listy nie rozszerza się ArrayList- jest to inny rodzaj obiektu. ArrayLists mają swoją własną tablicę wewnętrzną, w której przechowują swoje elementy i mogą zmieniać rozmiar tablic wewnętrznych itp. Opakowanie nie ma własnej tablicy wewnętrznej, a jedynie propaguje operacje do podanej tablicy.

  2. Z drugiej strony, jeśli później utworzysz nową tablicę jako

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

    następnie tworzysz nowy ArrayList, który jest pełną, niezależną kopią oryginału. Chociaż tutaj tworzysz opakowanie również za pomocą Arrays.asList, jest ono używane tylko podczas budowy nowego ArrayListi jest później zbierane jako śmieci. Struktura tego nowego ArrayListjest całkowicie niezależna od oryginalnej tablicy. Zawiera te same elementy (zarówno oryginalna tablica, jak i nowe ArrayListodwołanie, te same liczby całkowite w pamięci), ale tworzy nową, wewnętrzną tablicę, która przechowuje odwołania. Więc kiedy tasujesz, dodajesz, usuwasz elementy itp., Oryginalna tablica pozostaje niezmieniona.

Petr Pudlák
źródło
9
@Dineshkumar Opakowanie to wzorzec projektowy, który tłumaczy jeden interfejs klasy na inny. Zobacz artykuł o wzorach opakowań . | Gdzie musisz się upokorzyć? Sugerowałbym użycie List<Integer>dla typów zmiennych (lub argumentów metod itp.). Dzięki temu Twój kod jest bardziej ogólny i możesz łatwo przełączyć się na inną Listimplementację w razie potrzeby, bez konieczności przepisywania dużej ilości kodu.
Petr Pudlák
To dobre wyjaśnienie. Rozszerzając to pytanie, metoda Arrays.asList () również przyjmuje varargs. Jeśli przekażę określone wartości, powiedzmy, że zrobię: Arrays.asList (1,3,5) dwa razy, czy zwróci tę samą listę?
sbsatter
27

A to dlatego, że ArrayListwynikający z Arrays.asList()nie jest tego typu java.util.ArrayList. Arrays.asList()tworzy ArrayListrodzaj, java.util.Arrays$ArrayListktóry się nie rozszerza, java.util.ArrayLista jedynie rozszerzajava.util.AbstractList

Chris
źródło
9
List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));  //copy

W tym przypadku list1jest typu ArrayList.

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

Tutaj lista jest zwracana jako Listwidok, co oznacza, że ​​zawiera tylko metody dołączone do tego interfejsu. Dlatego niektóre metody nie są dozwolone list2.

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

Tutaj tworzysz nowy ArrayList. Po prostu przekazujesz mu wartość w konstruktorze. To nie jest przykład castingu. W castingu może wyglądać bardziej tak:

ArrayList list1 = (ArrayList)Arrays.asList(ia);
Krzysztof
źródło
4

Jestem tu dość późno, w każdym razie czułem, że wyjaśnienie z odniesieniami do doktora byłoby lepsze dla kogoś szukającego odpowiedzi.

  1. java.util.Arrays
  • Jest to klasa narzędziowa zawierająca kilka statycznych metod do działania na danej tablicy
  • asList jest jedną z takich statycznych metod, która pobiera tablicę wejściową i zwraca obiekt java.util.Arrays.ArrayList, który jest statyczną klasą zagnieżdżoną, która rozszerza AbstractList, która implementuje interfejs List.
  • Tak więc Arrays.asList (inarray) zwraca opakowanie List wokół tablicy wejściowej, ale to opakowanie to java.util.Arrays.ArrayList, a nie java.util.ArrayList i odwołuje się do tej samej tablicy, więc dodanie większej liczby elementów do tablicy opakowanej List miałoby wpływ na orignal, a także nie możemy zmienić długości.
  1. java.util.ArrayList
  • ArrayList ma kilka przeciążonych konstruktorów

    public ArrayList () - // zwraca arraylist o domyślnej pojemności 10

    public ArrayList (Kolekcja c)

    public ArrayList (int initialCapacity)

  • Więc kiedy przekażemy zwrócony obiekt Arrays.asList, tj. List (AbstractList) do drugiego konstruktora powyżej, utworzy on nową tablicę dynamiczną (ta wielkość tablicy rośnie, gdy dodajemy więcej elementów niż jej pojemność, a także nowe elementy nie wpłyną na tablicę oryginalną ) płytkie kopiowanie oryginalnej tablicy ( płytka kopia oznacza, że ​​kopiuje tylko referencje i nie tworzy nowego zestawu tych samych obiektów co w oryginalnej tablicy)

Gautam Tadigoppula
źródło
4
String names[] = new String[]{"Avinash","Amol","John","Peter"};
java.util.List<String> namesList = Arrays.asList(names);

lub

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

Powyższa instrukcja dodaje opakowanie do tablicy wejściowej. Zatem metody takie jak add & remove nie będą miały zastosowania do obiektu referencyjnego listy „namesList”.

Jeśli spróbujesz dodać element do istniejącej tablicy / listy, otrzymasz „Wyjątek w wątku” główny „java.lang.UnsupportedOperationException”.

Powyższa operacja jest tylko do odczytu lub tylko do przeglądania.
Nie możemy wykonać operacji dodawania lub usuwania w obiekcie listy. Ale

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

lub

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

W powyższej instrukcji utworzyłeś konkretną instancję klasy ArrayList i przekazałeś listę jako parametr.

W tym przypadku metoda add & remove będzie działać poprawnie, ponieważ obie metody pochodzą z klasy ArrayList, więc tutaj nie otrzymamy żadnego UnSupportedOperationException.
Zmiany wprowadzone w obiekcie Arraylist (metoda dodająca lub usuwająca element z / z listy arraylist) nie zostaną odzwierciedlone w oryginalnym obiekcie 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
Avinash Pande
źródło
3

Przede wszystkim klasa Arrays jest klasą narzędziową, która zawiera nie. metod narzędziowych do działania na Arrays (dzięki klasie Arrays w przeciwnym razie musielibyśmy stworzyć własne metody do działania na obiektach Array)

asList (), metoda:

  1. asListmetoda jest jedną z narzędzi Arrayklasy, jest metodą statyczną, dlatego możemy wywołać tę metodę po nazwie klasy (np. Arrays.asList(T...a))
  2. Teraz jest zwrot akcji, zwróć uwagę, że ta metoda nie tworzy nowego ArrayListobiektu, po prostu zwraca odniesienie List do istniejącego Arrayobiektu (więc teraz po użyciu asListmetody Arrayzostaną utworzone dwa odniesienia do istniejącego obiektu)
  3. i to jest powód, dla którego wszystkie metody, które działają na Listobiekcie, mogą NIE działać na tym obiekcie Array przy użyciu Listreferencji, jak na przykład Arrayrozmiar s ma stałą długość, stąd oczywiście nie możesz dodawać ani usuwać elementów z Arrayobiektu za pomocą tego Listodniesienia (jak list.add(10)lub list.remove(10);w przeciwnym razie zgłosi UnsupportedOperationException)
  4. każda zmiana, którą robisz za pomocą odwołania do listy, zostanie odzwierciedlona w wyjściu z Arrayobiektu s (tak jak operujesz na istniejącym obiekcie Array przy użyciu odwołania do listy)

W pierwszym przypadku tworzysz nowy Arraylistobiekt (w drugim przypadku tworzone jest tylko odniesienie do istniejącego obiektu Array, ale nie nowy ArrayListobiekt), więc teraz są dwa różne obiekty, jeden jest Arrayobiektem, a drugi jest ArrayListobiektem i nie ma między nimi połączenia (więc zmienia się w jednym obiekcie nie zostanie odbity / zmieniony w innym obiekcie (to jest w przypadku 2 ArrayiArraylist są to dwa różne obiekty)

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

przypadek 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));
irfan
źródło
3

Wiele osób już udzieliło odpowiedzi na szczegóły mechaniczne, ale warto zauważyć: to zły wybór projektowy, według Javy.

asListMetoda Java jest udokumentowana jako „Zwraca listę o stałym rozmiarze ...”. Jeśli weźmiesz jego wynik i wywołasz (powiedzmy) .addmetodę, wyrzuci ona UnsupportedOperationException. To nieintuicyjne zachowanie! Jeśli metoda mówi, że zwraca a List, standardowym oczekiwaniem jest to, że zwraca obiekt, który obsługuje metody interfejsu List. Deweloper nie powinien zapamiętywać, które z wielu util.Listmetod tworzą pliki List, które w rzeczywistości nie obsługują wszystkichList metod.

Gdyby nazwali tę metodę asImmutableList, miałoby to sens. Albo gdyby metoda po prostu zwróciła rzeczywistą List(i skopiowała tablicę podstawową), miałoby to sens. Postanowili faworyzować zarówno wydajność w czasie wykonywania, jak i krótkie nazwy, kosztem naruszenia zarówno zasady najmniejszej niespodzianki, jak i dobrej praktyki OO unikania UnsupportedOperationExceptions.

(Również projektanci mogli zrobić interface ImmutableList, aby uniknąć mnóstwa UnsupportedOperationExceptions.)

nie tylko yeti
źródło
2

Zauważ, że w Javie 8, „ia” powyżej musi być liczbą całkowitą [], a nie int []. Arrays.asList () tablicy int zwraca listę z jednym elementem. Podczas korzystania z fragmentu kodu OP kompilator wychwyci problem, ale niektóre metody (np. Collections.shuffle ()) po cichu nie będą działać zgodnie z oczekiwaniami.

Carl Burke
źródło
1
Napotkałem ten problem z kompilatorem, kiedy zrobiłem ArrayList <Integer> al = new ArrayList <Integer> (Arrays.asList (a)); gdzie a był int []. Moje al ma tylko jeden element, który również po wydrukowaniu wyglądał na śmieci. Co to za element? A jak to się dzieje, że tam przybywa?
Spotkałem się
@JyotsanaNandwani Pls sprawdź moją odpowiedź: 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
źródło
1

Arrays.asList()

ta metoda zwraca własną implementację List, pobiera tablicę jako argument i buduje na niej metody i atrybuty, ponieważ nie kopiuje żadnych danych z tablicy, ale używając oryginalnej tablicy powoduje to zmianę w oryginalnej tablicy podczas modyfikacji lista zwrócona przez Arrays.asList()metodę.

z drugiej strony.
ArrayList(Arrays.asList()); jest konstruktorem ArrayListklasy, która przyjmuje listę jako argument i zwraca wartość ArrayListniezależną od listy, tj. Arrays.asList()w tym przypadku przekazane jako argument. dlatego widzisz te wyniki;

Kunal Singh Gusain
źródło
0
1.List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));  //copy
2.List<Integer> list2 = Arrays.asList(ia);

W linii 2 Arrays.asList(ia)zwraca Listodwołanie do zdefiniowanego wewnątrz obiektu klasy wewnętrznej Arrays, który jest również wywoływany, ArrayListale jest prywatny i tylko rozszerza AbstractList. Oznacza to, że to, co zostało zwrócone z, Arrays.asList(ia)jest obiektem klasy innym niż ten, z którego otrzymujesz new ArrayList<Integer>.

Nie możesz użyć niektórych operacji do linii 2, ponieważ wewnętrzna klasa prywatna wewnątrz Arrays nie zapewnia tych metod.

Spójrz na ten link i zobacz, co możesz zrobić z prywatną klasą wewnętrzną: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ Arrays.java # Arrays.ArrayList

Linia 1 tworzy nowy ArrayListobiekt kopiując elementy z tego, co otrzymujesz z linii 2. Możesz więc robić, co chcesz, ponieważ java.util.ArrayListzapewnia wszystkie te metody.

zek_w
źródło
0

Podsumowanie różnic -

gdy lista jest tworzona bez użycia nowego operatora Arrays.asList () metoda zwraca Wrapper, co oznacza

1. możesz wykonać operację dodawania / aktualizacji.

2. zmiany dokonane w oryginalnej tablicy zostaną również odzwierciedlone w Liście i odwrotnie.

Raj
źródło
0

W odpowiedzi na kilka komentarzy zadających pytania dotyczące zachowania Arrays.asList () od wersji 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);
GŁUPEK
źródło