2012-05-08 32 views
7

Tôi có một lớp có trong hàm tạo của nó một danh sách các đối tượng, List<Object>. Mỗi lần danh sách có thể được tạo thành từ các yếu tố từ loại khác nhau. Đó là một cái gì đó chung chung, và tôi không biết loại lớp của họ sẽ là gì.Sao chép danh sách các đối tượng theo giá trị trong Java

Tôi muốn lưu bản sao của danh sách đó, trước khi cho phép người dùng thay đổi giá trị của nó. Nhưng vì bản sao được thực hiện bằng tham chiếu, cả hai danh sách (bản gốc và bản sao) đang được thay đổi ...

Làm cách nào để sao chép danh sách của mình theo giá trị?

Cảm ơn, Devora

+0

Hãy xem http://stackoverflow.com/questions/869033/how-do-i-copy-an-object-in-java – DonCallisto

+0

Bạn có nghĩa là danh sách mới của bạn sẽ không thay đổi nếu các mục được thêm/xóa trong danh sách gốc? Hay bạn cũng muốn đảm bảo rằng các mục trong danh sách của bạn không thể sửa đổi được? – assylias

Trả lời

1

Bạn cần phải tạo ra một mới List và sau đó thêm một bản sao của mỗi phần tử của danh sách ban đầu với nó.

List<Object> copy = new ArrayList<Object>(inputList.size()); 
for (Object item : inputList) { 
    copy.add(item.clone()); 
} 

Rõ ràng, bạn cần kiểm tra xem các đối tượng trong danh sách của bạn là Cloneable nếu không chúng sẽ không được sao chép chính xác.

+0

Đối tượng không có phương thức sao chép. – Dvora

+1

@Dvora ofcourse nó! http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#clone() – dogbane

+0

Chỉ cần lưu ý rằng clone() có thể là một nguồn của các vấn đề (xem câu trả lời của tôi để biết thêm chi tiết). –

-2

Nếu bạn có:

List<Object> objects 

Bạn có thể sao chép danh sách này bằng cách:

List<Object> newObjects = new ArrayList<Object>(objects); 

[Cập nhật: thêm thực hiện thực sự của ArrayList để chứng minh điều đó sao chép các đối tượng mới]

Xin xem thực hiện thực sự của ArrayList, nó sao chép các đối tượng - không chứa các tham chiếu đến cùng một đối tượng.

public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); size = elementData.length; if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); }

public Object[] toArray() { return Arrays.copyOf(elementData, size); }

+0

Như đã đề cập trong bài viết khác, điều này không sao chép các đối tượng; mảng mới chứa tham chiếu đến cùng một đối tượng. –

+0

@NeilCoffey Tôi đã xóa câu trả lời nhưng tôi nghĩ rằng câu hỏi này ít rõ ràng hơn về vấn đề là gì và liệu có cần phải có bản sao sâu hay không. Điều này rõ ràng không xứng đáng với một downvote vì nó có thể là câu trả lời đúng. cf Câu trả lời của Stephen C. – assylias

+0

OK, tôi nghĩ rằng nó đã được khá rõ ràng họ không muốn sao chép bằng cách tham khảo bởi vì họ trích dẫn rằng như là một vấn đề.Tuy nhiên, tôi thừa nhận câu hỏi không phải là từ rõ ràng nhất trên thế giới, vì vậy nếu tôi sai lầm xấu của tôi :) –

0

Duyệt qua danh sách thông qua và Crone nội dung của nó để tạo ra một danh sách mới mà sau đó có thể được sử dụng mà không thay đổi danh sách gốc.

private List<Object> clone; 
public Foo(List<Object> original) 
{ 
    this.clone = new ArrayList<Object>(list.size()); 
    for(Object item: original) 
    { 
    clone.add(item.clone()); 
    } 
} 
0

Nếu đối tượng của bạn không chứa bất kỳ tài liệu tham khảo khác của đối tượng khác, bạn có thể sử dụng Object.clone(), nhưng nếu không như vậy, bạn cần phải thực hiện một bản sao sâu.

2

Câu hỏi của bạn không rõ ràng.

  • Nếu bạn muốn có một bản sao cạn (tức là một bản sao của danh sách đó sẽ chứa tham chiếu đến các đối tượng trong danh sách ban đầu, sau đó phương pháp clone sẽ thực hiện công việc, như sẽ một constructor sao chép trên thực hiện List Nếu bạn muốn có một bản sao sâu (tức là một bản sao của danh sách chứa các bản sao của các đối tượng gốc) thì tốt nhất là tạo một danh sách mới và điền nó với các bản sao của các thành phần danh sách ban đầu. Phương pháp clone không được cung cấp theo mặc định, rất nhiều lớp tiện ích phổ biến được cloneable, bởi các lớp tùy chỉnh không phải là ... bạn đã thực hiện nó.Nếu danh sách của bạn có chứa bất kỳ đối tượng nào không phải cloneable, bạn sẽ nhận được ngoại lệ khi chạy.

Có các phương án sao chép sâu khác để clone, nhưng giống như nhân bản chúng không hoạt động với tất cả các lớp. Và thực sự, đó là mấu chốt của vấn đề nếu giải pháp của bạn phải là chung chung.

0

Trong các loại tình huống này, có ba giải pháp chung:

(1) Nếu bạn biết rằng các yếu tố cá nhân có clone() phương pháp mà làm việc như bạn có ý định, tạo ra một mảng mới và điền nó với bản sao() của mỗi phần tử tương ứng. Theo nguyên tắc chung, các đối tượng "cơ bản" từ JDK chẳng hạn như Strings, các biểu diễn số, v.v. sẽ ổn. Một bất lợi của clone() là nó là một chút của một 'pháo lỏng': bạn không biết một ưu tiên chính xác độ sâu 'bản sao' sẽ là nếu phương pháp đã bị ghi đè cho một lớp cụ thể và những tham chiếu nào có khả năng tái sử dụng hay không. Nó cũng có một nguy cơ cụ thể là rằng các lớp con của các đối tượng có thể nhân bản dựa trên logic trong hàm khởi tạo của chúng có trách nhiệm không hoạt động. Nhưng như tôi đã nói, các đối tượng JDK đơn giản sẽ ổn.

(2) Nối tiếp danh sách vào ObjectOutputStream được bao quanh một ByteArrayOutputStream. Sau đó lấy mảng byte kết quả, và bọc một ByteArrayInputStream và ObjectInputStream xung quanh nó và đọc từ đó một bản sao mới sâu của danh sách.

(3) Trong một thế giới lý tưởng, hãy tạo một danh sách mới, điền vào một danh sách mới, điền vào nó được tạo rõ ràng "bản sao" của các đối tượng được đề cập. Trường hợp nó không quá cồng kềnh với mã, đây là giải pháp lý tưởng vì bạn 'biết những gì đang thực sự xảy ra': sẽ không có bất ngờ tiềm ẩn nào về các đối tượng vô tình được tham chiếu lại thay vì được tạo lại bởi vì ví dụ: phương thức clone() không hoạt động như bạn mong đợi.

Khi bạn có đối tượng chứa đơn giản (danh sách mảng) có cấu trúc bạn có thể dễ dàng tạo lại, thì (2) có thể quá mức cần thiết. Nó có thể thuận tiện cho việc tái tạo các cấu trúc phức tạp hơn.

Re (1), bạn phải là đặc biệt đáng ngờ khi sao chép các đối tượng từ thư viện của bên thứ ba nơi các đối tượng không được thiết kế đặc biệt với bản sao(). Vấn đề là mọi người dễ dàng phân lớp lớp JDK (cloneable) hoặc phương thức clone() khác mà không thực sự suy nghĩ chi tiết - theo kinh nghiệm của tôi, clone() có một số tinh tế mà nhiều nhà phát triển không nhận thức được (ví dụ như các nhà xây dựng không nhận được gọi trên đối tượng mới) mà có thể dẫn đến các vấn đề unanticipated. Vì vậy, nếu bạn không thể nhìn thấy mã nguồn đáng tin cậy để chắc chắn rằng bản sao() sẽ an toàn, tôi sẽ tránh nó.

Các vấn đề liên quan