Sự khác biệt giữa Arrays.asList (mảng) và ArrayList <Integer> mới (Arrays.asList (mảng))


119

Sự khác biệt giữa

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

nơi ialà mảng các số nguyên.

Tôi đã biết rằng một số hoạt động không được phép trong list2. Tại sao nó như vậy? nó được lưu trữ trong bộ nhớ như thế nào (tham chiếu / sao chép)?

Khi tôi xáo trộn danh sách, list1không ảnh hưởng đến mảng ban đầu nhưng list2có. Nhưng vẫn còn list2hơi khó hiểu.

Làm thế nào ArrayListđược upcasted để khác với danh sách từ việc tạo mớiArrayList

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

2
Tôi khuyên bạn nên xem xét tùy chọn của Google Guava . Lists.newArrayList(ia)tạo một bản sao độc lập, giống như tùy chọn đầu tiên. Nó chỉ đơn giản là tổng quát hơn và tốt hơn để xem xét.
qben

Câu trả lời:


228
  1. Trước tiên, hãy xem điều này làm gì:

    Arrays.asList(ia)

    Nó lấy một mảng iavà tạo một trình bao bọc để triển khai List<Integer>, làm cho mảng ban đầu có sẵn dưới dạng danh sách. Không có gì được sao chép và tất cả, chỉ một đối tượng trình bao bọc duy nhất được tạo. Các hoạt động trên trình bao bọc danh sách được truyền sang mảng ban đầu. Điều này có nghĩa là nếu bạn xáo trộn trình bao bọc danh sách, mảng ban đầu cũng được xáo trộn, nếu bạn ghi đè một phần tử, nó sẽ bị ghi đè trong mảng ban đầu, v.v. Tất nhiên, một số Listthao tác không được phép trên trình bao bọc, như thêm hoặc xóa các phần tử khỏi danh sách, bạn chỉ có thể đọc hoặc ghi đè các phần tử.

    Lưu ý rằng trình bao bọc danh sách không mở rộng ArrayList- nó là một loại đối tượng khác. ArrayLists có mảng nội bộ của riêng chúng, trong đó chúng lưu trữ các phần tử của chúng và có thể thay đổi kích thước các mảng bên trong, v.v ... Trình bao bọc không có mảng bên trong của riêng nó, nó chỉ truyền các hoạt động đến mảng được cấp cho nó.

  2. Mặt khác, nếu sau đó bạn tạo một mảng mới như

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

    sau đó bạn tạo mới ArrayList, là bản sao đầy đủ, độc lập với bản gốc. Mặc dù ở đây bạn cũng tạo trình bao bọc bằng cách sử dụng Arrays.asList, nhưng nó chỉ được sử dụng trong quá trình xây dựng trình bao bọc mới ArrayListvà được thu gom sau đó. Cấu trúc của ArrayListmảng mới này hoàn toàn độc lập với mảng ban đầu. Nó chứa các phần tử giống nhau (cả mảng ban đầu và ArrayListtham chiếu mới này đều là các số nguyên giống nhau trong bộ nhớ), nhưng nó tạo ra một mảng bên trong mới, chứa các tham chiếu. Vì vậy, khi bạn xáo trộn nó, thêm, bớt các phần tử, v.v., mảng ban đầu không thay đổi.


9
@Dineshkumar Một trình bao bọc là một mẫu thiết kế chuyển một giao diện cho một lớp thành một giao diện khác. Xem bài viết về mẫu trình bao bọc . | Bạn cần upcast ở đâu? Tôi khuyên bạn nên sử dụng List<Integer>cho các loại biến của bạn (hoặc các đối số phương thức, v.v.). Điều này làm cho mã của bạn chung chung hơn và bạn có thể dễ dàng chuyển sang một Listtriển khai khác nếu cần mà không cần phải viết lại nhiều mã.
Petr Pudlák

Đó là một lời giải thích tốt. Mở rộng câu hỏi này, phương thức Arrays.asList () cũng nhận varargs. Nếu tôi chuyển các giá trị cụ thể, giả sử tôi thực hiện: Arrays.asList (1,3,5) hai lần, nó có trả về cùng một Danh sách không?
sbsatter

27

Điều này là do ArrayListkết quả từ Arrays.asList()không phải là loại java.util.ArrayList. Arrays.asList()tạo ra một ArrayListkiểu java.util.Arrays$ArrayListkhông mở rộng java.util.ArrayListmà chỉ mở rộngjava.util.AbstractList


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

Trong trường hợp này, list1là loại ArrayList.

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

Ở đây, danh sách được trả về dưới dạng một dạng Listxem, có nghĩa là nó chỉ có các phương thức được đính kèm với giao diện đó. Do đó, tại sao một số phương thức không được phép bật list2.

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

Đây, bạn đang tạo một cái mới ArrayList. Bạn chỉ đơn giản là chuyển cho nó một giá trị trong hàm tạo. Đây không phải là một ví dụ về đúc. Trong quá trình truyền, nó có thể trông giống như sau:

ArrayList list1 = (ArrayList)Arrays.asList(ia);

4

Tôi đến đây khá muộn, dù sao cũng cảm thấy một lời giải thích với tài liệu tham khảo sẽ tốt hơn cho ai đó đang tìm kiếm câu trả lời.

  1. java.util.Arrays
  • Đây là một lớp tiện ích với một loạt các phương thức tĩnh để hoạt động trên mảng nhất định
  • asList là một trong những phương thức tĩnh như vậy nhận mảng đầu vào và trả về một đối tượng của java.util.Arrays.ArrayList là một lớp lồng nhau tĩnh mở rộng AbstractList mà inturn thực hiện giao diện Danh sách.
  • Vì vậy, Arrays.asList (inarray) trả về một trình bao bọc Danh sách xung quanh mảng đầu vào nhưng trình bao bọc này là java.util.Arrays.ArrayList chứ không phải java.util.ArrayList và nó tham chiếu đến cùng một mảng nên việc thêm nhiều phần tử hơn vào mảng được bao bọc bởi Danh sách sẽ ảnh hưởng orignal một cũng vậy và chúng tôi cũng không thể thay đổi độ dài.
  1. java.util.ArrayList
  • ArrayList có một loạt các hàm tạo quá tải

    public ArrayList () - // trả về danh sách mảng với dung lượng mặc định là 10

    public ArrayList (Bộ sưu tập c)

    public ArrayList (int initialCapacity)

  • Vì vậy, khi chúng ta truyền đối tượng trả về Arrays.asList tức là List (AbstractList) đến hàm tạo thứ hai ở trên, nó sẽ tạo ra một mảng động mới (kích thước mảng này tăng lên khi chúng ta thêm nhiều phần tử hơn dung lượng của nó và các phần tử mới sẽ không ảnh hưởng đến mảng gốc ) sao chép cạn mảng ban đầu ( sao chép cạn có nghĩa là nó chỉ sao chép qua các tham chiếu và không tạo một tập hợp mới gồm các đối tượng giống như trong mảng gốc)


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

hoặc là

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

Câu lệnh trên thêm trình bao bọc trên mảng đầu vào. Vì vậy, các phương thức như add & remove sẽ không được áp dụng trên đối tượng tham chiếu danh sách 'namesList'.

Nếu bạn cố gắng thêm một phần tử trong mảng / danh sách hiện có thì bạn sẽ nhận được "Ngoại lệ trong luồng" main "java.lang.UnsupportedOperationException".

Thao tác trên là chỉ đọc hoặc chỉ xem.
Chúng tôi không thể thực hiện thêm hoặc bớt thao tác trong đối tượng danh sách. Nhưng

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

hoặc là

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

Trong câu lệnh trên, bạn đã tạo một phiên bản cụ thể của lớp ArrayList và chuyển một danh sách làm tham số.

Trong trường hợp này, phương thức add & remove sẽ hoạt động bình thường vì cả hai phương thức đều từ lớp ArrayList nên ở đây chúng ta sẽ không nhận được bất kỳ UnSupportedOperationException nào.
Các thay đổi được thực hiện trong đối tượng Arraylist (phương thức thêm hoặc xóa một phần tử trong / khỏi danh sách mảng) sẽ không phản ánh trong đối tượng java.util.List ban đầu.

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

Trước hết, lớp Mảng là một lớp tiện ích không chứa. của các phương thức tiện ích để hoạt động trên Mảng (nhờ lớp Mảng nếu không, chúng ta sẽ cần tạo các phương thức của riêng mình để hoạt động trên các đối tượng Mảng)

phương thức asList ():

  1. asListmethod là một trong những method tiện ích của Arrayclass, nó là static method đó là lý do tại sao chúng ta có thể gọi method này bằng tên lớp của nó (như Arrays.asList(T...a))
  2. Bây giờ là bước ngoặt, xin lưu ý rằng phương thức này không tạo ArrayListđối tượng mới , nó chỉ trả về một tham chiếu Danh sách cho Arrayđối tượng hiện có (vì vậy bây giờ sau khi sử dụng asListphương thức, hai tham chiếu đến Arrayđối tượng hiện có sẽ được tạo)
  3. và đây là lý do, tất cả các phương thức hoạt động trên Listđối tượng, KHÔNG thể hoạt động trên đối tượng Mảng này bằng cách sử dụng Listtham chiếu, chẳng hạn như Arraykích thước s là cố định về độ dài, do đó bạn rõ ràng không thể thêm hoặc xóa các phần tử khỏi Arrayđối tượng bằng cách sử dụng Listtham chiếu này (như list.add(10)hoặc list.remove(10);nếu không nó sẽ ném UnsupportedOperationException)
  4. bất kỳ thay đổi nào bạn đang thực hiện bằng cách sử dụng tham chiếu danh sách sẽ được phản ánh trong việc thoát Arrayđối tượng s (khi bạn đang hoạt động trên đối tượng Mảng hiện có bằng cách sử dụng tham chiếu danh sách)

Trong trường hợp đầu tiên bạn đang tạo một Arraylistđối tượng mới (trong trường hợp thứ hai, chỉ tham chiếu đến đối tượng Mảng hiện có được tạo chứ không phải một ArrayListđối tượng mới ), vì vậy bây giờ có hai đối tượng khác nhau, một là Arrayđối tượng và một là ArrayListđối tượng và không có kết nối giữa chúng (vì vậy thay đổi trong một đối tượng sẽ không bị phản ánh / ảnh hưởng trong một đối tượng khác (đó là trong trường hợp 2 ArrayArraylistlà hai đối tượng khác nhau)

trường hợp 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));

trường hợp 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

Nhiều người đã trả lời các chi tiết cơ khí rồi, nhưng cần lưu ý: Đây là một lựa chọn thiết kế kém, bởi Java.

asListPhương thức của Java được ghi là "Trả về một danh sách có kích thước cố định ...". Nếu bạn lấy kết quả của nó và gọi (nói) .addphương thức, nó sẽ ném ra một UnsupportedOperationException. Đây là hành vi không trực quan! Nếu một phương thức cho biết nó trả về a List, kỳ vọng tiêu chuẩn là nó trả về một đối tượng hỗ trợ các phương thức của giao diện List. Một nhà phát triển không cần phải ghi nhớ phương thức nào trong số mười util.Listphương thức tạo ra những phương thức Listkhông thực sự hỗ trợ tất cả các Listphương thức.

Nếu họ đã đặt tên cho phương pháp asImmutableList, nó sẽ có ý nghĩa. Hoặc nếu họ chỉ có phương thức trả về một thực tế List(và sao chép mảng hỗ trợ), nó sẽ có ý nghĩa. Họ đã quyết định ưu tiên cả hiệu suất thời gian chạy tên viết tắt, với cái giá là vi phạm cả Nguyên tắc Ít bất ngờ và Thực tiễn tốt về việc tránh UnsupportedOperationExceptions.

(Ngoài ra, các nhà thiết kế có thể đã thực hiện interface ImmutableList, để tránh rất nhiều UnsupportedOperationException.)


2

Lưu ý rằng, trong Java 8, 'ia' ở trên phải là Integer [] chứ không phải int []. Arrays.asList () của một mảng int trả về một danh sách với một phần tử duy nhất. Khi sử dụng đoạn mã của OP, trình biên dịch sẽ giải quyết được vấn đề, nhưng một số phương thức (ví dụ: Collections.shuffle ()) sẽ không thực hiện được những gì bạn mong đợi.


1
Tôi gặp phải sự cố trình biên dịch này khi thực hiện ArrayList <Integer> al = new ArrayList <Integer> (Arrays.asList (a)); trong đó a là một int []. Al của tôi cũng chỉ có một phần tử duy nhất khi được in ra trông như rác. Nguyên tố đó là gì? Và làm thế nào nó xảy ra để đến đó? Tôi phải đối mặt với vấn đề này trong Java 7
Jyotsana Nandwani

@JyotsanaNandwani Vui lòng kiểm tra câu trả lời của tôi: 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());
        }
    }
}

1

Arrays.asList()

Phương thức này trả về việc triển khai List của chính nó. Nó lấy một mảng làm đối số và xây dựng các phương thức và thuộc tính trên đó, vì nó không sao chép bất kỳ dữ liệu nào từ một mảng mà sử dụng mảng gốc, điều này gây ra sự thay đổi trong mảng gốc khi bạn sửa đổi danh sách trả về bởi Arrays.asList()phương thức.

Mặt khác.
ArrayList(Arrays.asList()); là một phương thức khởi tạo của ArrayListlớp lấy một danh sách làm đối số và trả về một đối số ArrayListđộc lập với danh sách tức là. Arrays.asList()trong trường hợp này được chuyển như một đối số. đó là lý do tại sao bạn thấy những kết quả này;


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

Trong dòng 2, Arrays.asList(ia)trả về một Listtham chiếu của đối tượng lớp bên trong được định nghĩa bên trong Arrays, đối tượng này cũng được gọi ArrayListnhưng là riêng tư và chỉ mở rộng AbstractList. Điều này có nghĩa là những gì trả về từ Arrays.asList(ia)một đối tượng lớp khác với những gì bạn nhận được từ đó new ArrayList<Integer>.

Bạn không thể sử dụng một số thao tác cho dòng 2 vì lớp private bên trong Arrayskhông cung cấp các phương thức đó.

Hãy xem liên kết này và xem bạn có thể làm gì với lớp bên trong riêng tư: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ Arrays.java # Arrays.ArrayList

Dòng 1 tạo một ArrayListđối tượng mới sao chép các phần tử từ những gì bạn nhận được từ dòng 2. Vì vậy, bạn có thể làm bất cứ điều gì bạn muốn vì java.util.ArrayListcung cấp tất cả các phương thức đó.


0

Tóm tắt sự khác biệt -

khi danh sách được tạo mà không sử dụng phương thức toán tử mới Arrays.asList (), nó sẽ trả về Wrapper có nghĩa là

1. bạn có thể thực hiện thêm / cập nhật hoạt động.

2. những thay đổi được thực hiện trong mảng ban đầu cũng sẽ được phản ánh vào Danh sách và ngược lại.


0

Đáp lại một số nhận xét đặt câu hỏi về hoạt động của Arrays.asList () kể từ 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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.