Bản sao ArrayList Java


214

Tôi có một ArrayList l1kích thước 10. Tôi gán l1cho loại tham chiếu danh sách mới l2. Sẽ l1l2chỉ vào cùng một ArrayListđối tượng? Hoặc là một bản sao của ArrayListđối tượng được gán cho l2?

Khi sử dụng l2tham chiếu, nếu tôi cập nhật đối tượng danh sách, nó cũng phản ánh các thay đổi trong l1loại tham chiếu.

Ví dụ:

List<Integer> l1 = new ArrayList<Integer>();
for (int i = 1; i <= 10; i++) {
    l1.add(i);
}

List l2 = l1;
l2.clear();

Không có cách nào khác để gán một bản sao của một đối tượng danh sách cho một biến tham chiếu mới, ngoài việc tạo 2 đối tượng danh sách và thực hiện sao chép trên các bộ sưu tập từ cũ sang mới?

Câu trả lời:


459

Có, gán sẽ chỉ sao chép giá trị của l1(là tham chiếu) vào l2. Cả hai sẽ đề cập đến cùng một đối tượng.

Tạo một bản sao nông khá dễ dàng:

List<Integer> newList = new ArrayList<>(oldList);

(Chỉ là một ví dụ.)


1
Có thể chỉ sao chép một phần của danh sách mảng sang danh sách mảng mới, Hiệu quả. ví dụ: sao chép các phần tử giữa vị trí 5 và 10 từ một danh sách mảng sang một danh sách mảng mới khác. Trong ứng dụng của tôi, phạm vi sẽ lớn hơn nhiều.
Ashwin

1
@Ashwin: Vâng, đó là thao tác O (N), nhưng vâng ... bạn có thể sử dụng List.subListđể có được "lượt xem" trên một phần của danh sách gốc.
Jon Skeet

Nếu danh sách mảng được lồng nhau (ArrayList<ArrayList<Object>>)thì sao? điều này sẽ đệ quy tạo các bản sao của tất cả các đối tượng ArrayList con?
Mèo

3
@Cat: Không ... Đây chỉ là một bản sao nông.
Jon Skeet

1
@ShanikaEdiriweera: Bạn có thể làm điều đó một cách trôi chảy với các luồng, vâng. Nhưng phần khó khăn là tạo ra một bản sao sâu, mà hầu hết các đối tượng sẽ không cung cấp. Nếu bạn có một trường hợp cụ thể trong đầu, tôi khuyên bạn nên hỏi một câu hỏi mới với các chi tiết.
Jon Skeet

67

14
Quan tâm để giải thích tại sao điều này có thể thích hợp hơn new ArrayList<>(source);?
Alex

6
@atc đó là một cách nữa để thực hiện sao chép nông, thay vì ArrayList mới () nó đã sử dụng một thuật toán khác và có thể được sử dụng cho bất kỳ triển khai Danh sách nào, không chỉ cho một ArrayList, đó là tất cả :)
Sergii Zagriichuk

14
Phương pháp này rất sai lệch! Trên thực tế, đó là mô tả. Nó nói: "sao chép các phần tử từ một danh sách nguồn vào đích", nhưng chúng không được sao chép! Chúng được tham chiếu nên sẽ chỉ có 1 bản sao của các đối tượng và nếu chúng có thể thay đổi, bạn sẽ gặp rắc rối
ACV

5
không nơi nào trong java-api nhân bản sâu được thực hiện bởi bất kỳ lớp bộ sưu tập nào
Vikash

Câu trả lời này không có nhiều ý nghĩa. Collections.copyhoàn toàn không phải là một sự thay thế cho new ArrayList<>(source). Những gì Collections.copythực sự làm là giả định rằng destination.size()ít nhất là lớn như vậy source.size(), và sau đó sao chép phạm vi chỉ mục theo chỉ mục bằng cách sử dụng set(int,E)phương thức. Phương pháp không thêm các phần tử mới vào đích. Tham khảo mã nguồn nếu nó không đủ rõ ràng từ Javadoc.
Radiodef

35

l1l2sẽ trỏ đến cùng một tham chiếu, cùng một đối tượng.

Nếu bạn muốn tạo một ArrayList mới dựa trên ArrayList khác, bạn làm điều này:

List<String> l1 = new ArrayList<String>();
l1.add("Hello");
l1.add("World");
List<String> l2 = new ArrayList<String>(l1); //A new arrayList.
l2.add("Everybody");

Kết quả sẽ l1vẫn có 2 yếu tố và l2sẽ có 3 yếu tố.


Bạn có thể vui lòng giải thích sự khác biệt giữa List<String> l2 = new ArrayList<String>(l1)List<String> l2 = l1?
MortalMan

@MortalMan sự khác biệt là l2 = new ArrayList <String> (l1) là một đối tượng hoàn toàn mới và sửa đổi l2 không ảnh hưởng đến l1, trong khi Danh sách <String> l2 = l1 bạn không tạo đối tượng mới mà chỉ tham chiếu giống nhau đối tượng là l1, vì vậy trong trường hợp này thực hiện một thao tác như l2.add ("Mọi người"), l1.size () và l2.size () sẽ trả về 3 vì cả hai đều tham chiếu cùng một đối tượng.
Alfredo Osorio

19

Một cách thuận tiện khác để sao chép các giá trị từ src ArrayList sang Dest Arraylist như sau:

ArrayList<String> src = new ArrayList<String>();
src.add("test string1");
src.add("test string2");
ArrayList<String> dest= new ArrayList<String>();
dest.addAll(src);

Đây là sao chép thực tế của các giá trị và không chỉ sao chép tham chiếu.


10
Tôi không hoàn toàn chắc chắn điều này là chính xác. thử nghiệm của tôi cho thấy điều ngược lại (vẫn tham chiếu cùng một đối tượng)
invertigo

giải pháp này hiệu quả với tôi khi sử dụng ArrayList với
ArrayAd CHƯƠNG

1
Câu trả lời này là sai. addAll () chỉ sao chép các tham chiếu như invertigo đã nói. Đây không phải là một bản sao sâu sắc.
jk7

Đối với một ArrayList <String> câu trả lời này có thể chấp nhận được vì String là bất biến, nhưng hãy thử nó với ví dụ của OP, ArraList <Integer> và bạn sẽ thấy nó chỉ là sao chép các tham chiếu.
jk7

Tôi đoán không phải là ngày của tôi. Hóa ra các lớp như Integer và Long cũng không thay đổi, vì vậy câu trả lời của Harshal hoạt động cho các trường hợp đơn giản như ArrayList <Integer> và ArrayList <String>. Trường hợp thất bại là đối với các đối tượng phức tạp không phải là bất biến.
jk7

8

Có một phương thức addAll () sẽ phục vụ mục đích sao chép One ArrayList sang một cái khác.

Ví dụ: bạn có hai Danh sách mảng: sourceListtargetList , sử dụng mã bên dưới.

targetList.add ALL (sourceList);


nó cũng chỉ sao chép tài liệu tham khảo.
Vikash

4

Java không truyền đối tượng, nó chuyển các tham chiếu (con trỏ) đến các đối tượng. Vì vậy, có, l2 và l1 là hai con trỏ đến cùng một đối tượng.

Bạn phải tạo một bản sao rõ ràng nếu bạn cần hai danh sách khác nhau có cùng nội dung.


3
Làm thế nào để bạn thực hiện "một bản sao rõ ràng"? Tôi cho rằng bạn đang nói về một bản sao sâu?
Cin316

1

List.copyOf List danh sách không thể thay đổi

Bạn đã hỏi:

Có cách nào khác để gán một bản sao của danh sách không

Java 9 đã mang đến các List.ofphương thức sử dụng các chữ để tạo ra một Listlớp cụ thể không xác định được.

LocalDate today = LocalDate.now( ZoneId.of( "Africa/Tunis" ) ) ;
List< LocalDate > dates = List.of( 
    today.minusDays( 1 ) ,  // Yesterday
    today ,                 // Today
    today.plusDays( 1 )     // Tomorrow
);

Cùng với đó chúng tôi cũng có List.copyOf. Phương thức này cũng trả về một Listlớp bê tông không xác định.

List< String > colors = new ArrayList<>( 4 ) ;          // Creates a modifiable `List`. 
colors.add ( "AliceBlue" ) ;
colors.add ( "PapayaWhip" ) ;
colors.add ( "Chartreuse" ) ;
colors.add ( "DarkSlateGray" ) ;
List< String > masterColors = List.copyOf( colors ) ;   // Creates an unmodifiable `List`.

Bằng cách không thể thay đổi được, chúng tôi có nghĩa là số lượng phần tử trong danh sách và tham chiếu đối tượng được giữ trong mỗi vị trí dưới dạng một phần tử, được cố định. Bạn không thể thêm, thả hoặc thay thế các yếu tố. Nhưng đối tượng tham chiếu được giữ trong mỗi phần tử có thể hoặc không thể thay đổi .

colors.remove( 2 ) ;          // SUCCEEDS. 
masterColors.remove( 2 ) ;    // FAIL - ERROR.

Xem này chạy trực tiếp tại IdeOne.com .

ngày.toString (): [2020/02/02, 2020/02/03, 2020/02/2016]

colors.toString (): [AliceBlue, PapayaWhip, DarkSlateGray]

masterColors.toString (): [AliceBlue, PapayaWhip, Chartreuse, DarkSlateGray]

Bạn hỏi về tài liệu tham khảo đối tượng. Như những người khác đã nói, nếu bạn tạo một danh sách và gán nó cho hai biến tham chiếu (con trỏ), bạn vẫn chỉ có một danh sách. Cả hai đều chỉ vào cùng một danh sách. Nếu bạn sử dụng một trong hai con trỏ để sửa đổi danh sách, cả hai con trỏ sau đó sẽ thấy các thay đổi, vì chỉ có một danh sách trong bộ nhớ.

Vì vậy, bạn cần phải tạo một bản sao của danh sách. Nếu bạn muốn bản sao đó không thể thay đổi, hãy sử dụng List.copyOfphương pháp như được thảo luận trong Câu trả lời này. Trong phương pháp này, bạn kết thúc với hai danh sách riêng biệt, mỗi danh sách có các phần tử giữ tham chiếu đến cùng các đối tượng nội dung. Ví dụ, trong ví dụ của chúng tôi ở trên sử dụng Stringcác đối tượng để thể hiện màu sắc, các đối tượng màu đang trôi nổi trong bộ nhớ ở đâu đó. Hai danh sách giữ con trỏ đến cùng một đối tượng màu. Đây là một sơ đồ.

nhập mô tả hình ảnh ở đây

Danh sách đầu tiên colorscó thể sửa đổi. Điều này có nghĩa là một số phần tử có thể được loại bỏ như đã thấy trong đoạn mã trên, trong đó chúng tôi đã loại bỏ phần tử thứ 3 ban đầu Chartreuse(chỉ số 2 = thứ 3). Và các yếu tố có thể được thêm vào. Và các yếu tố có thể được thay đổi để trỏ đến một số khác Stringnhư OliveDrabhoặc CornflowerBlue.

Ngược lại, bốn yếu tố masterColorsđược cố định. Không loại bỏ, không thêm, và không thay thế màu khác. Việc Listthực hiện đó là không thể thay đổi.

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.