2009-03-20 35 views
63

Có bất kỳ tiện ích cho nhân bản sâu sắc đối với các bộ sưu tập java:Sâu bản sao tiện ích giới thiệu

  • Mảng
  • Lists
  • Maps

LƯU Ý: thích một số giải pháp mà không sử dụng tuần tự, nhưng với việc sử dụng phương thức Object.clone(). Tôi có thể chắc chắn rằng đối tượng tùy chỉnh của tôi sẽ thực hiện phương pháp clone() và sẽ chỉ sử dụng các lớp học java tiêu chuẩn được cloneable ...

+0

bản sao có thể có của [Làm thế nào để bạn tạo bản sao sâu của một đối tượng trong Java?] (Http://stackoverflow.com/questions/64036/how-do-you-make-a-deep-copy-of- an-object-in-java) –

Trả lời

51

Tôi nghĩ câu trả lời màu xanh lá cây trước đó là xấu, tại sao bạn có thể hỏi?

  • Nó cho biết thêm nhiều mã
  • Nó đòi hỏi bạn phải liệt kê tất cả các lĩnh vực để được sao chép và làm điều này
  • này sẽ không làm việc cho Lists khi sử dụng clone() (Đây là những gì clone() cho HashMap cho biết: Trả về bản sao nông của cá thể HashMap này: các khóa và giá trị không được nhân bản.) để bạn kết thúc bằng tay (điều này làm tôi khóc)

Oh và theo cách serialization cũng xấu , bạn có thể phải thêm Serializable trên khắp nơi (điều này cũng làm cho tôi cr y).

Vì vậy, giải pháp là gì:

Java sâu nhân bản thư viện Thư viện sao chép là một nhỏ, mã nguồn mở (giấy phép apache) thư viện java mà sâu nhái đối tượng. Các đối tượng không phải thực hiện giao diện Cloneable. Có hiệu lực, thư viện này có thể sao chép bất kỳ đối tượng java nào. Nó có thể được sử dụng tức là trong việc triển khai bộ nhớ cache nếu bạn không muốn đối tượng được lưu trữ bị sửa đổi hoặc bất cứ khi nào bạn muốn tạo một bản sao sâu của các đối tượng.

Cloner cloner=new Cloner(); 
XX clone = cloner.deepClone(someObjectOfTypeXX); 

Check it out tại https://github.com/kostaskougios/cloning

+3

Cloner là một thư viện tuyệt vời (chỉ có hiệu suất của nó đôi khi làm cho tôi khóc ... nhưng tôi đoán bạn không thể làm tốt hơn nhiều với sự phản ánh) – mik01aj

+0

Giải pháp này có vẻ tốt hơn. Đã không thử nó, nhưng có vài ý kiến ​​trong chủ đề này rất tích cực về cloner. – Juraj

+1

Không hoạt động trên Android ... – wieczorek1990

9

Một cách chung để sao chép sâu một bộ sưu tập tùy ý là nối tiếp bộ sưu tập đó vào luồng, sau đó đọc lại nó vào bộ sưu tập mới. Bạn sẽ được bù nước hoàn toàn các đối tượng hoàn toàn mới không có bất kỳ mối quan hệ nào với các đối tượng cũ, ngoài việc là các bản sao giống hệt nhau.

Kiểm tra Câu trả lời của Bruno để có liên kết đến Apache Commons serialization utility classes, điều này sẽ rất hữu ích nếu đây là tuyến đường bạn quyết định thực hiện.

+0

Nối tiếp giải pháp là tốt, nhưng tôi nghĩ về một cái gì đó mà không có nó. Tôi có thể garantie rằng đối tượng tùy chỉnh của tôi sẽ được nhân bản một cách chính xác với phương thức clone(), nhưng tôi muốn trợ giúp sẽ làm điều đó cho các lớp java chuẩn ... – Juraj

+0

Cách tuần tự hóa để sao chép là tốt, nhưng tôi có một số trường không dưới sự kiểm soát của tôi và không được tuần tự hóa ... – Juraj

5

Một khả năng là sử dụng serialization:

Apache Commons cung cấp SerializationUtils

0

Sử dụng serialization và sau đó deserialization, nhưng lưu ý rằng phương pháp này chỉ có tác dụng với các lớp Serializable mà không lĩnh vực thoáng qua. Ngoài ra, những người độc thân của bạn sẽ không còn là những người độc thân nữa.

+0

Việc sử dụng serialization và deserialization cho công việc nhân bản các đối tượng bộ nhớ trong thời gian chạy, hoặc giữa các lần chạy riêng biệt của một chương trình là một ý tưởng tồi. Để biết thêm thông tin về lý do tại sao điều này là, google: "tại sao serialization và deserialization là xấu". –

18

Tất cả các phương pháp để sao chép các đối tượng trong Java có sai sót nghiêm trọng:

Clone

  1. Các clone() phương pháp được bảo vệ, vì vậy bạn không thể gọi nó là trực tiếp trừ khi lớp trong ghi đè câu hỏi nó với một phương pháp công cộng.
  2. clone() không gọi hàm tạo. Bất kỳ nhà xây dựng nào. Nó sẽ cấp phát bộ nhớ, gán trường nội bộ class (mà bạn có thể đọc qua getClass()) và sao chép các trường của tài liệu gốc.

Đối với vấn đề hơn với clone(), xem mục 11 của cuốn sách Joshua Bloch "Effective Java, Second Edition"

Serialize

Serialize thậm chí còn tồi tệ hơn; nó có nhiều lỗ hổng của clone() và sau đó một số. Joshua có cả một chương với bốn mục cho chủ đề này một mình.

Giải pháp của tôi

Giải pháp của tôi là thêm một giao diện mới cho các dự án của tôi:

public interface Copyable<T> { 
    T copy(); 
    T createForCopy(); 
    void copyTo (T dest); 
} 

Mã này trông như thế này:

class Demo implements Copyable<Demo> { 
    public Demo copy() { 
     Demo copy = createForCopy(); 
     copyTo (copy); 
     return copy; 
    } 
    public Demo createForCopy() { 
     return new Demo(); 
    } 
    public void copyTo (Demo dest) 
     super.copyTo (dest); 
     ...copy fields of Demo here... 
    } 
} 

Thật không may, tôi phải sao chép này mã cho tất cả các đối tượng của tôi nhưng nó luôn là cùng một mã, vì vậy tôi có thể sử dụng một mẫu trình soạn thảo Eclipse. Ưu điểm:

  1. Tôi có thể quyết định trình tạo nào cần gọi và cách khởi tạo trường nào.
  2. Khởi xảy ra theo một thứ tự xác định (lớp gốc đến lớp chẳng hạn)
  3. tôi có thể tái sử dụng đối tượng hiện có và ghi đè lên chúng
  4. Loại an toàn
  5. Singletons ở độc thân

Đối với các loại Java tiêu chuẩn (như bộ sưu tập, vv), tôi sử dụng một lớp tiện ích có thể sao chép chúng. Các phương thức có cờ và callbacks, vì vậy tôi có thể kiểm soát độ sâu của một bản sao.

+2

Tôi đã làm một cái gì đó tương tự bằng cách thực hiện clone() trong tất cả các lớp mà tôi cần phải sao chép. Vấn đề lớn nhất là nếu tôi có bộ sưu tập, tôi phải lặp lại nó và sao chép nó bằng chính mình ... – Juraj

+0

Sử dụng chức năng trợ giúp chấp nhận Bộ sưu tập và trả về một ArrayList: Vì bạn biết kích thước, bộ nhớ sẽ cấp phát bộ nhớ một lần và ArrayList nhanh cho các kiểu truy cập thông thường. –

+0

createForCopy cần trả lại một Demo – TimP

15

Shallow nhân bản một bộ sưu tập rất dễ dàng, nhưng nếu bạn muốn nhân bản sâu, thư viện có thể sẽ làm bạn tốt hơn so với tay mã hóa nó (kể từ khi bạn muốn sao chép các yếu tố bên trong bộ sưu tập nữa).

Giống như this answer, tôi đã sử dụng Cloner library và đặc biệt hiệu suất đã thử nghiệm nó với XStream (có thể 'nhân bản' bằng cách tuần tự hóa sau đó deserializing) và tuần tự nhị phân. Mặc dù xStream rất nhanh tại serializing đến/từ xml, Cloner là nhanh hơn nhiều tại nhân bản:

0,0851 ms: XStream (clone bởi serializing/deserializing)
0,0223 ms: serialization nhị phân (clone bởi serializing/deserializing)
0,000017 ms: cloner
* thời gian trung bình để sao chép một đối tượng đơn giản (hai trường) và không có hàm tạo mặc định, công khai. Chạy 10.000 lần.

Ngoài việc là nhanh, ở đây có nhiều lý do để lựa chọn Cloner:

  1. thực hiện một bản sao sâu của bất kỳ đối tượng (ngay cả những bạn không viết cho mình)
  2. bạn không có để giữ cho phương pháp của bạn clone() up-to-date mỗi khi bạn thêm một lĩnh vực
  3. bạn có thể sao chép đối tượng mà không có một mặc định constructor nào
  4. làm việc với mùa xuân
  5. (tối ưu hóa) doesn 't nhân bản các vật thể bất biến được biết đến (như Integer, String, v.v.)
  6. dễ sử dụng. Ví dụ:

    cloner.deepClone (anyObject);

+1

tổng quan đẹp, cảm ơn – kostja

11

Tôi là tác giả của thư viện cloner, là thư mà Brad trình bày. Đây là một giải pháp để nhân bản các đối tượng mà không phải viết thêm bất kỳ mã nào (không cần đối tượng serializable hoặc phương thức clone())

Nó khá nhanh như Brad đã nói, và gần đây tôi đã tải lên một phiên bản thậm chí còn nhanh hơn. Lưu ý rằng việc thực hiện thủ công một phương thức clone() sẽ nhanh hơn việc sao chép lib, nhưng sau đó một lần nữa bạn sẽ cần phải viết nhiều mã.

Cloner lib đã hoạt động khá tốt đối với tôi vì tôi đang sử dụng nó trong việc triển khai bộ nhớ cache cho một trang web có lưu lượng rất lớn (~ 1 triệu yêu cầu/ngày). Bộ đệm nên sao chép khoảng 10 đối tượng theo yêu cầu. Nó khá đáng tin cậy và ổn định. Nhưng xin lưu ý rằng nhân bản không phải là không có rủi ro. Các lib có thể được cấu hình để println mỗi cá thể lớp mà nó nhân bản trong dev. Bằng cách này bạn có thể kiểm tra xem nó có nhân bản những gì bạn nghĩ rằng nó sẽ sao chép không - đồ thị đối tượng có thể rất sâu và có thể chứa các tham chiếu đến một số lượng lớn các đối tượng đáng ngạc nhiên. Với bản sao lib, bạn có thể hướng dẫn nó không sao chép các đối tượng mà bạn không muốn, ví dụ như những người độc thân.

+0

Thư viện có vẻ tốt, sẽ kiểm tra lại sau ... – Juraj

+0

"Với bản sao lib, bạn có thể hướng dẫn không sao chép các đối tượng bạn không muốn" điều này, nhưng không thể, làm thế nào để tôi yêu cầu nó không sao chép các lĩnh vực nhất định? – Sudarshan

+0

Chúc mừng Konstantinos. Tôi đang sử dụng thư viện của bạn thay vì Apache utils một (Đã có vấn đề với phôi với SerializableUtils). Bạn đang làm việc mà không có vấn đề gì. Tuyệt vời! – will824

2

Tôi đã sử dụng thư viện cloning này và thấy nó khá hữu ích. Vì nó có một vài hạn chế (tôi cần kiểm soát hạt mịn hơn trong quá trình nhân bản: trường nào, trong ngữ cảnh nào và độ sâu nên được nhân bản vv), tôi đã tạo phiên bản mở rộng của nó. Bạn kiểm soát nhân bản của các trường bằng cách chú thích chúng trong lớp thực thể.

Chỉ cần để có được một hương vị của nó, đây là một lớp dụ:

public class CloneMePlease { 
    @Clone(Skip.class) 
    String id3 = UUID.randomUUID().toString(); 

    @Clone(Null.class) 
    String id4 = UUID.randomUUID().toString(); 

    @Clone(value = RandomUUID.class, groups=CustomActivationGroup1.class) 
    String id5 = UUID.randomUUID().toString(); 

    @Clone.List({ 
      @Clone(groups=CustomActivationGroup2.class, value=Skip.class), 
      @Clone(groups=CustomActivationGroup3.class, value=Copy.class)}) 
    Object activationGroupOrderTest = new Object(); 

    @Clone(LongIncrement.class) 
    long version = 1l; 

    @PostClone 
    private void postClone(CloneMePlease original, @CloneInject CloneInjectedService service){ 
     //do stuff with the original source object in the context of the cloned object 
     //you can inject whatewer service you want, from spring/guice to perform custom logic here 
    } 
} 

Xem thêm chi tiết ở đây: https://github.com/mnorbi/fluidity-cloning

Ngoài ra còn có một phần mở rộng cụ thể ngủ đông trong trường hợp người ta cần nó.