2008-09-15 12 views
226

जावा में एक गहरी ऑब्जेक्ट कॉपी फ़ंक्शन को कार्यान्वित करना थोड़ा मुश्किल है। मूल वस्तु सुनिश्चित करने के लिए आप क्या कदम उठाते हैं और क्लोन किए गए किसी का कोई संदर्भ नहीं है?आप जावा में किसी ऑब्जेक्ट की गहरी प्रति कैसे बनाते हैं?

+4

Kryo [नकल/क्लोनिंग] के लिए निर्मित समर्थन किया है (https://code.google.com/p/kryo/#Copying/cloning)। यह ऑब्जेक्ट से ऑब्जेक्ट पर ऑब्जेक्ट कॉपी करता है, ऑब्जेक्ट-> बाइट्स-> ऑब्जेक्ट नहीं। – NateS

+1

यहां एक संबंधित प्रश्न है जिसे बाद में पूछा गया था: [डीप क्लोन उपयोगिता पुनर्मूल्यांकन] (http://stackoverflow.com/q/665860/152061) –

उत्तर

135

ऑब्जेक्ट को क्रमबद्ध करने के लिए एक सुरक्षित तरीका है, फिर deserialize। यह सुनिश्चित करता है कि सबकुछ एक नया नया संदर्भ है।

Here's an article यह कुशलतापूर्वक कैसे करें।

चेतावनी: कक्षाओं के लिए धारावाहिक को ओवरराइड करना संभव है जैसे कि नए उदाहरण बनाए गए हैं, उदा। सिंगलेट्स के लिए। यदि आपकी कक्षाएं Serializable नहीं हैं तो यह भी काम नहीं करता है।

+5

ध्यान रखें कि लेख में प्रदान किया गया FastByteArrayOutputStream कार्यान्वयन अधिक कुशल हो सकता है। जब बफर भर जाता है तो यह एक ऐरेलिस्ट-स्टाइल विस्तार का उपयोग करता है, लेकिन लिंक्डलिस्ट-शैली विस्तार दृष्टिकोण का उपयोग करना बेहतर होता है। एक नया 2x बफर बनाने और वर्तमान बफर को memcpy-ing बनाने के बजाय, बफर की एक लिंक्ड सूची बनाए रखें, वर्तमान में भरने पर एक नया जोड़ना। यदि आपको अपने डिफ़ॉल्ट बफर आकार में फ़िट होने से अधिक डेटा लिखने का अनुरोध मिलता है, तो बफर नोड बनाएं जो अनुरोध के बराबर है; नोड्स को एक ही आकार की आवश्यकता नहीं है। –

+0

बस क्रियो का उपयोग करें: https://github.com/EsotericSoftware/kryo#copyingcloning बेंचमार्क http://www.slideshare.net/AlexTumanoff/serialization-and-performance – zengr

+0

एक अच्छा लेख, जो धारावाहिककरण के माध्यम से गहरी प्रतिलिपि बताता है: http : //www.javaworld.com/article/2077578/learn-java/java-tip-76--an-alternative-to-the-deep-copy-technique.html –

6

दीप प्रतिलिपि केवल प्रत्येक वर्ग की सहमति के साथ ही की जा सकती है। यदि आपके पास कक्षा पदानुक्रम पर नियंत्रण है तो आप क्लोनबल इंटरफ़ेस को कार्यान्वित कर सकते हैं और क्लोन विधि को कार्यान्वित कर सकते हैं। अन्यथा एक गहरी प्रतिलिपि करना सुरक्षित रूप से करना असंभव है क्योंकि ऑब्जेक्ट गैर-डेटा संसाधनों (जैसे डेटाबेस कनेक्शन) साझा भी कर सकता है। सामान्य रूप से हालांकि, जावा पर्यावरण में गहरी प्रतिलिपि को खराब अभ्यास माना जाता है और उचित डिजाइन प्रथाओं के माध्यम से टालना चाहिए।

+1

क्या आप "उचित डिजाइन प्रथाओं" का वर्णन कर सकते हैं? – eftokay83

32

आप कॉमर्स लैंग में org.apache.commons.lang3.SerializationUtils.clone(T) का उपयोग करके एक क्रमबद्ध-आधारित गहरे क्लोन कर सकते हैं, लेकिन सावधान रहें - प्रदर्शन अबाध है।

सामान्य रूप से, क्लोनिंग की आवश्यकता वाले ऑब्जेक्ट ग्राफ़ में ऑब्जेक्ट के प्रत्येक वर्ग के लिए अपने स्वयं के क्लोन विधियों को लिखना सबसे अच्छा अभ्यास है।

8

एक्सस्ट्रीम (http://x-stream.github.io/) का उपयोग करें। आप यह भी नियंत्रित कर सकते हैं कि आप कौन सी गुणों को एनोटेशन के माध्यम से अनदेखा कर सकते हैं या स्पष्ट रूप से संपत्ति नाम को एक्सस्ट्रीम क्लास को निर्दिष्ट कर सकते हैं। इसके अलावा आपको क्लोनबल इंटरफेस को लागू करने की आवश्यकता नहीं है।

11

एक्सस्ट्रीम ऐसे मामलों में वास्तव में उपयोगी है। यहाँ

private static final XStream XSTREAM = new XStream(); 
... 

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj)); 
+0

Nooo, आपको ऑब्जेक्ट को xml-ing के ओवरहेड की आवश्यकता नहीं है। – egelev

+0

@egeleve आपको एहसास है कि आप '08 से एक टिप्पणी का जवाब दे रहे हैं? मैं अब जावा का उपयोग नहीं करता हूं और शायद अब बेहतर उपकरण हैं। हालांकि उस समय, एक अलग प्रारूप के लिए क्रमबद्ध करने और फिर serializing वापस एक अच्छा हैक की तरह लग रहा था - यह निश्चित रूप से अक्षम था। – sankara

57

क्लोनिंग कुछ लोगों का उपयोग कर या अधिभावी Object.clone() उल्लेख किया है करने के लिए एक सरल कोड है। ऐसा मत करो Object.clone() में कुछ प्रमुख समस्याएं हैं, और अधिकांश मामलों में इसका उपयोग निराश होता है। पूर्ण उत्तर के लिए जोशुआ ब्लोच द्वारा "Effective Java" से आइटम 11 देखें। मेरा मानना ​​है कि आप प्राइमेटिव टाइप एरे पर सुरक्षित रूप से Object.clone() का उपयोग कर सकते हैं, लेकिन इसके अलावा आपको क्लोन का सही उपयोग और ओवरराइड करने के बारे में उचित होने की आवश्यकता है।

सीरियलाइजेशन (एक्सएमएल या अन्यथा) पर निर्भर योजनाएं क्लूजी हैं।

यहां कोई आसान जवाब नहीं है। यदि आप किसी ऑब्जेक्ट को गहरी प्रतिलिपि बनाना चाहते हैं तो आपको ऑब्जेक्ट ग्राफ़ को पार करना होगा और प्रत्येक बच्चे ऑब्जेक्ट को ऑब्जेक्ट की कॉपी कन्स्ट्रक्टर या एक स्थिर फैक्ट्री विधि के माध्यम से स्पष्ट रूप से कॉपी करना होगा जो बदले में बच्चे की वस्तु को गहरी प्रतिलिपि बनाता है। Immutables (उदा। String एस) कॉपी करने की आवश्यकता नहीं है। एक तरफ के रूप में, आपको इस कारण से अपरिवर्तनीयता का पक्ष लेना चाहिए।

6
import com.thoughtworks.xstream.XStream; 

public class deepCopy { 
    private static XStream xstream = new XStream(); 

    //serialize with Xstream them deserialize ... 
    public static Object deepCopy(Object obj){ 
     return xstream.fromXML(xstream.toXML(obj)); 
    } 
} 
44

आप फाइलें बनाने के बिना क्रमबद्धता के साथ एक गहरी प्रतिलिपि बना सकते हैं।

आपकी ऑब्जेक्ट जो आप गहरी प्रतिलिपि बनाना चाहते हैं उसे implement serializable की आवश्यकता होगी। यदि कक्षा अंतिम नहीं है या संशोधित नहीं की जा सकती है, तो वर्ग का विस्तार करें और धारावाहिक लागू करें।

बाइट्स की एक धारा को अपनी कक्षा में कनवर्ट करें:

ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
ObjectOutputStream oos = new ObjectOutputStream(bos); 
oos.writeObject(object); 
oos.flush(); 
oos.close(); 
bos.close(); 
byte[] byteData = bos.toByteArray(); 

बाइट्स की एक धारा से अपने कक्षा पुनर्स्थापित: गहरी प्रतिलिपि को लागू करने के

ByteArrayInputStream bais = new ByteArrayInputStream(byteData); 
(Object) object = (Object) new ObjectInputStream(bais).readObject(); 
+4

यदि कक्षा अंतिम है तो आप इसे कैसे बढ़ाएंगे? –

+1

@ कुमारमारिश क्लास MyContainer Serializable {MyFinalClass उदाहरण लागू करता है; ...} –

+0

मुझे यह एक अच्छा जवाब मिल गया। क्लोन एक गड़बड़ है – blackbird014

22

एक तरह से प्रत्येक जुड़े करने के लिए प्रति कंस्ट्रक्टर्स जोड़ने के लिए है कक्षा। एक प्रतिलिपि निर्माता 'इस' का एक उदाहरण अपने एकल तर्क के रूप में लेता है और इसके सभी मूल्यों की प्रतिलिपि बनाता है। काफी कुछ काम, लेकिन बहुत सरल और सुरक्षित।

संपादित करें: ध्यान दें कि फ़ील्ड पढ़ने के लिए आपको एक्सेसर विधियों का उपयोग करने की आवश्यकता नहीं है। आप सीधे सभी क्षेत्रों तक पहुंच सकते हैं क्योंकि स्रोत उदाहरण प्रतिलिपि निर्माता के साथ उदाहरण के समान ही होता है। स्पष्ट लेकिन अनदेखा किया जा सकता है।

उदाहरण:

public class Order { 

    private long number; 

    public Order() { 
    } 

    /** 
    * Copy constructor 
    */ 
    public Order(Order source) { 
     number = source.number; 
    } 
} 


public class Customer { 

    private String name; 
    private List<Order> orders = new ArrayList<Order>(); 

    public Customer() { 
    } 

    /** 
    * Copy constructor 
    */ 
    public Customer(Customer source) { 
     name = source.name; 
     for (Order sourceOrder : source.orders) { 
      orders.add(new Order(sourceOrder)); 
     } 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 
} 

संपादित करें: ध्यान दें कि जब प्रति कंस्ट्रक्टर्स का उपयोग कर आप वस्तु आप कॉपी कर रहे के क्रम प्रकार पता करने की जरूरत। उपर्युक्त दृष्टिकोण के साथ आप आसानी से मिश्रित सूची की प्रतिलिपि नहीं बना सकते हैं (आप इसे कुछ प्रतिबिंब कोड के साथ करने में सक्षम हो सकते हैं)।

+1

इस मामले में बस दिलचस्पी है कि आप जो कॉपी कर रहे हैं वह उप-वर्ग है, लेकिन माता-पिता द्वारा संदर्भित किया जा रहा है। क्या कॉपी कन्स्ट्रक्टर को ओवरराइड करना संभव है? –

+0

आपकी मूल कक्षा अपने उप-वर्ग का संदर्भ क्यों देती है? क्या आप एक उदाहरण दे सकते हैं? –

+1

पब्लिक क्लास कार वाहन और फिर वाहन के रूप में कार का जिक्र करती है। originaList = नया ArrayList ; कॉपीलिस्ट = नया ऐरेलिस्ट ; originalList.add (नई कार()); (वाहन वाहन: वाहन सूची) { copyList.add (नया वाहन (वाहन)); } –

9

जेएसओएन को जटिल जावा ऑब्जेक्ट को क्रमबद्ध करने और इसे वापस पढ़ने के लिए जैक्सन जेएसओएन का उपयोग करना एक बहुत ही आसान और सरल तरीका है।

http://wiki.fasterxml.com/JacksonInFiveMinutes

+0

लिंक मर चुका है। –

12

आप use a library एक सरल एपीआई है, और प्रतिबिंब के साथ अपेक्षाकृत तेजी से क्लोनिंग करता है कि (क्रमबद्धता तरीकों की तुलना में तेजी से होना चाहिए) कर सकते हैं।

Cloner cloner = new Cloner(); 

MyClass clone = cloner.deepClone(o); 
// clone is a deep-clone of o 
4

मैं जावा वस्तुओं क्लोनिंग के लिए Dozer का इस्तेमाल किया और यह है कि कम से बहुत अच्छा है, Kryo पुस्तकालय एक और बढ़िया विकल्प है।

14

अपाचे कॉमन्स एक वस्तु को गहरे क्लोन के लिए एक तेज़ तरीका प्रदान करता है।

My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1); 
+0

एक परीक्षण और स्थिर लाइब्रेरी का सुझाव देने के लिए अपवोट – katzenhut

2

BeanUtils एक बहुत अच्छा काम गहरी क्लोनिंग सेम करता है।

BeanUtils.cloneBean(obj); 
+2

यह उथले क्लोनिंग करता है। –

2

1)

public static Object deepClone(Object object) { 
    try { 
    ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
    ObjectOutputStream oos = new ObjectOutputStream(baos); 
    oos.writeObject(object); 
    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 
    ObjectInputStream ois = new ObjectInputStream(bais); 
    return ois.readObject(); 
    } 
    catch (Exception e) { 
    e.printStackTrace(); 
    return null; 
    } 
} 

2) 

    // (1) create a MyPerson object named Al 
    MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India"); 
    MyPerson al = new MyPerson("Al", "Arun", address); 

    // (2) make a deep clone of Al 
    MyPerson neighbor = (MyPerson)deepClone(al); 

यहाँ अपने MyPerson और MyAddress वर्ग serilazable इंटरफ़ेस

3

को लागू करना चाहिए जटिल वस्तुओं के लिए और मैं क्रमानुसार करने एक json लाइब्रेरी का उपयोग जब प्रदर्शन महत्वपूर्ण नहीं है gson की तरह, जेसन पाठ के लिए ऑब्जेक्ट, फिर नई ऑब्जेक्ट प्राप्त करने के लिए टेक्स्ट को deserialize।

जीसन जो प्रतिबिंब पर आधारित है, ज्यादातर मामलों में काम करेगा, सिवाय इसके कि transient फ़ील्ड कॉपी नहीं किए जाएंगे और कारण StackOverflowError के साथ परिपत्र संदर्भ वाले ऑब्जेक्ट्स नहीं होंगे।

public static <T> T Copy(T AnObject, Class<T> ClassInfo) 
{ 
    Gson gson = new GsonBuilder().create(); 
    String text = gson.toJson(AnObject); 
    T newObject = gson.fromJson(text, ClassInfo); 
    return newObject; 
} 
public static void main(String[] args) 
{ 
    String originalObject = "hello"; 
    String copiedObject = Copy(originalObject, String.class); 

} 
+1

कृपया अपने और हमारे लिए जावा नामकरण सम्मेलनों का पालन करें। –

4

स्प्रिंग फ्रेमवर्क उपयोगकर्ताओं के लिए।वर्ग org.springframework.util.SerializationUtils का उपयोग करना:

@SuppressWarnings("unchecked") 
public static <T extends Serializable> T clone(T object) { 
    return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object)); 
} 
संबंधित मुद्दे