Làm cách nào để sao chép Danh sách chung trong Java?


155

Tôi có một ArrayList<String>cái mà tôi muốn trả lại một bản sao. ArrayListcó một phương pháp nhân bản có chữ ký sau:

public Object clone()

Sau khi tôi gọi phương thức này, làm cách nào để chuyển đối tượng được trả về ArrayList<String>?


16
Không, đây là một câu hỏi hợp lệ. Java không hỗ trợ các tổng quát "thực", với kiểu xóa thời gian chạy và tất cả, vì vậy các loại chi tiết này có thể khó. Hơn nữa, giao diện Clonizable và cơ chế phương thức Object.clone () cũng gây nhầm lẫn tương tự.
Lập trình viên ngoài vòng pháp luật

OK, tôi chủ yếu làm C # trong đó điều này thực sự dễ dàng. Xin vui lòng cho tôi biết nếu bạn muốn tôi loại bỏ các ý kiến ​​từ câu hỏi này.
Espo

1
Bạn có thể để lại nhận xét. Tôi nghĩ rằng các chỉnh sửa của tôi đã giải thích những gì tôi đã gặp rắc rối.
Bill Lizard

2
Nhận xét của bạn là OK, nếu một chút hạ thấp. Tôi tưởng tượng rất nhiều hoops mà các nhà phát triển Java phải nhảy qua có vẻ ngớ ngẩn đối với các nhà phát triển .NET.
Lập trình viên ngoài vòng pháp luật

1
@Oscar, anh ấy muốn sao chép, và không gọi lệnh clone. Nó có thể không giống với bản sao mà bản sao không thực sự nhân bản. Tôi nghĩ rằng đây là điểm. Đây thực sự là một câu hỏi khó.
Rafa

Câu trả lời:


62
ArrayList newArrayList = (ArrayList) oldArrayList.clone();

43
Điều này sẽ hoạt động tốt cho String (đó là những gì câu hỏi yêu cầu), nhưng đáng chú ý là ArrayList.clone sẽ thực hiện một bản sao nông, vì vậy nếu có các đối tượng có thể thay đổi trong danh sách, chúng sẽ không được sao chép (và thay đổi một trong một danh sách sẽ thay đổi một trong các danh sách khác nữa.
pkaeding

49
Bạn nên tránh sử dụng các loại thô trong bất cứ điều gì ngoại trừ mã kế thừa. Bạn nên sử dụng tốt hơn ArrayList<String> newArrayList = (ArrayList<String>) oldArrayList.clone();.
cdmckay

20
Thật quá tệ khi ArrayList có phương thức #clone, nhưng List thì không. Thở dài.
rogerdpack

12
Không liên quan nhưng trong khi ở đó: sử dụng List<String>thay vì ArrayList<String>ở phía bên trái. Đây là cách các bộ sưu tập nên được sử dụng hầu hết thời gian.
Barshe Roussy

3
Tôi không thể sử dụng phương thức này để sao chép một ArrayList. Phương thức clone () không được công nhận.
Jack

318

Tại sao bạn muốn sao chép? Tạo một danh sách mới thường có ý nghĩa hơn.

List<String> strs;
...
List<String> newStrs = new ArrayList<>(strs);

Công việc hoàn thành.


17
Bạn có thể không biết loại Danh sách đó là gì. Nó có thể là LinkedList, MyOwnCustomList hoặc một lớp con của ArrayList, trong trường hợp đó, việc tạo một ArrayList sẽ là loại không chính xác.
Steve Kuo

51
Tôi có quan tâm đến việc thực hiện danh sách ban đầu được sử dụng? Tôi có thể quan tâm việc thực hiện danh sách mới sử dụng.
Tom Hawtin - tackline

12
@Steve Kuo: Chữ ký có ArrayList(Collection<? extends E> c)nghĩa là nó không quan trọng bạn sử dụng loại danh sách nào làm đối số.
cdmckay

3
Tôi có nghĩa là nó không phải là một bản sao sâu sắc, phải không?

4
@YekhezkelYigs Không, nó sẽ không như vậy.
Tom Hawtin - tackline

19

Đây là mã tôi sử dụng cho điều đó:

ArrayList copy = new ArrayList (original.size());
Collections.copy(copy, original);

Hy vọng là hữu ích cho bạn


3
Và tránh sử dụng các loại thô .. vì vậy thay vì ArrayListsử dụngArrayList<YourObject>
milosmns 17/12/14

2
docs.oracle.com/javase/8/docs/api/java/util/ khuyên Không hoạt động vì kích thước danh sách khác nhau.
MLProgrammer-CiM 30/03/2016

Tại sao bạn không sử dụng quá tải sao chép của hàm ArrayListtạo?
Tom Hawtin - tackline

19

Với Java 8, nó có thể được nhân bản với một luồng.

import static java.util.stream.Collectors.toList;

...

List<AnObject> clone = myList.stream().collect(toList());

Có thể được thực hiện như Danh sách <AnObject> xy = new ArrayList <> (oldList);
mirzak

1
Đây không phải là một bản sao sâu, những thay đổi trên các yếu tố của một danh sách có thể được nhìn thấy trong một
Inchara

2
Trường hợp trong câu hỏi nó chỉ định một bản sao sâu được yêu cầu? Giả định là một bộ sưu tập khác có chứa các đối tượng tương tự được yêu cầu. Nếu bạn muốn đi xuống tuyến đường sao chép sâu thì bạn đang mở ra một lon giun hoàn toàn mới.
Simon Jenkins

Không thực sự là một bản sao, phải không? Từ các tài liệu API "Không có đảm bảo nào về loại, tính dễ biến đổi, tính tuần tự hoặc tính an toàn của luồng của Danh sách được trả về". Anh không muốn một trong số đó. toCollectioncó thể là một lựa chọn tốt hơn
Tom Hawtin - tackline

16

Xin lưu ý rằng Object.clone () có một số vấn đề lớn và việc sử dụng nó không được khuyến khích trong hầu hết các trường hợp. Vui lòng xem Mục 11, từ " Java hiệu quả " của Joshua Bloch để có câu trả lời đầy đủ. Tôi tin rằng bạn có thể sử dụng Object.clone () một cách an toàn trên các mảng kiểu nguyên thủy, nhưng ngoài ra, bạn cần thận trọng về việc sử dụng và ghi đè đúng cách. Bạn có lẽ tốt hơn hết là xác định một hàm tạo sao chép hoặc một phương thức tĩnh của nhà máy mà nhân bản rõ ràng đối tượng theo ngữ nghĩa của bạn.


15

Tôi nghĩ rằng điều này nên thực hiện thủ thuật bằng cách sử dụng API Bộ sưu tập:

Lưu ý : phương pháp sao chép chạy trong thời gian tuyến tính.

//assume oldList exists and has data in it.
List<String> newList = new ArrayList<String>();
Collections.copy(newList, oldList);

13
Tại sao không sử dụng ArrayList mới <String> (oldList)?
cdmckay

4
Tôi tin rằng điều này sẽ không hoạt động, từ doc * Danh sách đích ít nhất phải dài bằng danh sách nguồn. Nếu dài hơn, các yếu tố còn lại trong danh sách đích không bị ảnh hưởng.
Greg Domjan

@GregDomjan không phải tôi đồng ý rằng đây là cách tốt nhất, nhưng đó là cách để làm điều đó. Để giải quyết vấn đề của bạn, nó giống như ví dụ sau: List <String> newList = new ArrayList <> (oldList.size ());
nckbrz

1
Không chắc nó có liên quan đến Java 8 hay không, nhưng ngay cả khi chỉ định kích thước, vẫn nhận được ngoại lệ IndexOutOfBoundException: Destination.size () <source.size (): 0 <2 trên List<MySerializableObject> copyList = new ArrayList<>(mMySerializableObjects.size()); Có vẻ như sử dụng copyList.addAll(original);là một thay thế tốt
Gene Bo

@GeneBo Nó không chỉ định kích thước. Đó là xác định năng lực, đó là một điều rất khác nhau. Niềm vui của những intcuộc tranh luận bí ẩn . Tôi không biết tại sao bạn muốn sử dụng phương pháp tĩnh tối nghĩa này thay vì cũ kỹaddAll .
Tom Hawtin - tackline

8

Tôi thấy việc sử dụng addAll hoạt động tốt.

ArrayList<String> copy = new ArrayList<String>();
copy.addAll(original);

dấu ngoặc đơn được sử dụng thay vì cú pháp tổng quát


5
Điều đó sẽ làm việc cho Chuỗi, nhưng không phải cho các đối tượng có thể thay đổi. Bạn sẽ muốn sao chép chúng là tốt.
jodonnell

Vâng, câu hỏi của anh ấy là dành cho chuỗi. Và anh ta có vấn đề về thuốc generic mà thực sự không thích đồ đúc trong trường hợp này.
Allain Lalonde

Ngoài ra, ArrayList.clone sẽ chỉ thực hiện một bản sao nông, vì vậy các đối tượng có thể thay đổi trong danh sách sẽ không được sao chép bằng phương thức đó.
pkaeding

Đó phải là ArrayList <String> btw. Ngoài ra, có lẽ bạn nên sử dụng ArrayList mới <String> (bản gốc) vì nó ít viết hơn và rõ ràng hơn.
cdmckay

4
Tại sao không chỉ sử dụng new ArrayList<String>(original)?
dùng102008

6

Nếu bạn muốn điều này để có thể trả về Danh sách trong một getter, tốt hơn là nên làm:

ImmutableList.copyOf(list);

4

Để sao chép một giao diện chung như java.util.Listbạn sẽ chỉ cần bỏ nó. đây là một ví dụ:

List list = new ArrayList();
List list2 = ((List) ( (ArrayList) list).clone());

Đó là một chút khó khăn, nhưng nó hoạt động, nếu bạn bị giới hạn trả lại một Listgiao diện, vì vậy bất cứ ai sau khi bạn có thể thực hiện danh sách của bạn bất cứ khi nào anh ta muốn.

Tôi biết câu trả lời này gần với câu trả lời cuối cùng, nhưng câu trả lời của tôi trả lời làm thế nào để làm tất cả những điều đó trong khi bạn đang làm việc với List- cha mẹ chung chung - không phảiArrayList


2
bạn đang giả định rằng nó sẽ luôn là một ArrayList không đúng
jucardi

@JuanCarlosDiaz sẽ, đây là câu hỏi được hỏi, đó là về ArrayList, vì vậy tôi đã trả lời nó cho ArrayList :)
Ahmed Hamdy

2

Hãy thật cẩn thận khi nhân bản ArrayLists. Nhân bản trong java là nông. Điều này có nghĩa là nó sẽ chỉ sao chép chính Arraylist chứ không phải các thành viên của nó. Vì vậy, nếu bạn có ArrayList X1 và sao chép nó vào X2, bất kỳ thay đổi nào trong X2 cũng sẽ xuất hiện trong X1 và ngược lại. Khi bạn sao chép, bạn sẽ chỉ tạo một ArrayList mới với các con trỏ tới cùng các phần tử trong bản gốc.


2

Điều này cũng sẽ làm việc:

ArrayList<String> orig = new ArrayList<String>();
ArrayList<String> copy = (ArrayList<String>) orig.clone()

2
List<String> shallowClonedList = new ArrayList<>(listOfStrings);

Hãy nhớ rằng đây chỉ là một bản sao nông, không phải là một bản sao sâu, tức là. bạn nhận được một danh sách mới, nhưng các mục giống nhau. Đây không phải là vấn đề cho các chuỗi đơn giản. Khó khăn hơn khi các mục trong danh sách là chính các đối tượng.


1
ArrayList first = new ArrayList ();
ArrayList copy = (ArrayList) first.clone ();

1

Tôi không phải là một chuyên gia java, nhưng tôi có cùng một vấn đề và tôi đã cố gắng giải quyết bằng phương pháp này. (Giả sử T có một hàm tạo sao chép).

 public static <T extends Object> List<T> clone(List<T> list) {
      try {
           List<T> c = list.getClass().newInstance();
           for(T t: list) {
             T copy = (T) t.getClass().getDeclaredConstructor(t.getclass()).newInstance(t);
             c.add(copy);
           }
           return c;
      } catch(Exception e) {
           throw new RuntimeException("List cloning unsupported",e);
      }
}

Không có gì đảm bảo rằng List lớp triển khai có một hàm tạo không có đối số công khai. Chẳng hạn, Lists trả về bởi Array.asList, List.ofhoặc List.subListcó thể không.
Tom Hawtin - tackline
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.