आप किसी वस्तु की गहरी प्रतिलिपि कैसे बनाते हैं?


301

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


4
क्रियो ने कॉपी / क्लोनिंग के लिए अंतर्निहित समर्थन किया है । यह ऑब्जेक्ट से ऑब्जेक्ट पर डायरेक्ट कॉपी होता है, ऑब्जेक्ट से नहीं-> बाइट्स-> ऑब्जेक्ट से।
नैट्स

1
यहां एक संबंधित प्रश्न है जो बाद में पूछा गया था: दीप क्लोन उपयोगिता
पुनर्संयोजन

क्लोनिंग लाइब्रेरी का उपयोग करने से मेरे लिए दिन बच गया! github.com/kostaskougios/cloning
गौरव

जवाबों:


168

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

यहाँ एक लेख है कि यह कैसे कुशलतापूर्वक किया जाए।

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


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


एक अच्छा लेख, जो सीरियलाइज़ेशन के माध्यम से गहरी प्रतिलिपि बताता है: javaworld.com/article/2077578/learn-java/…
विज्ञापन Infinitum

@BrianHarris लिंक्ड सूची गतिशील सरणी से अधिक कुशल नहीं है। डायनामिक एरे में तत्वों को सम्मिलित करना निरंतर जटिलता को
परिमित करता

कॉपी कंस्ट्रक्टर दृष्टिकोण की तुलना में कितना धारावाहिक और deserialize धीमा है?
वूलैंड

75

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

योजनाएँ जो क्रमबद्धता पर निर्भर करती हैं (XML या अन्यथा) कुडली हैं।

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


58

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

आपकी वस्तु जिसे आप कॉपी करना चाहते हैं, उसकी आवश्यकता होगी 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
@KumarManish class MyContainer लागू करता है Serializable {MyFinalClass उदाहरण; ...}
माटेओ टी।

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

@MatteoT। instanceइस मामले में गैर-परिवर्तनीय वर्ग की संपत्ति को कैसे अनुक्रमित किया जाएगा, गैर-सीरियल करने योग्य है?
फरीद

40

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

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


यह भी उपलब्ध हैorg.apache.commons.lang.SerializationUtils
Pino

25

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

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

उदाहरण:

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
बस इस मामले में दिलचस्पी है कि आप जो कॉपी कर रहे हैं वह एक उपवर्ग है, लेकिन माता-पिता द्वारा संदर्भित किया जा रहा है। क्या कॉपी कंस्ट्रक्टर को ओवरराइड करना संभव है?
पोर्क 'एन' बनी

आपका मूल वर्ग अपने उपवर्ग का संदर्भ क्यों देता है? क्या आप एक उदाहरण दे सकते हैं?
एड्रियन कोस्टर

1
सार्वजनिक श्रेणी की कार वाहन का विस्तार करती है और फिर गाड़ी को वाहन के रूप में संदर्भित करती है। ओरिजिनलिस्ट = नया एरियरिस्ट <वाहन>; copyList = नया ArrayList <वाहन>; originalList.add (नई कार ()); के लिए (वाहन वाहन: वाहन चालक) {copyList.add (नया वाहन (वाहन)); }
पोर्क 'एन' बनी

@ AdriaanKoster: यदि मूल सूची में ए है Toyota, तो आपका कोड Carगंतव्य सूची में डाल देगा । उचित क्लोनिंग के लिए आम तौर पर यह आवश्यक होता है कि वर्ग एक वर्चुअल फैक्ट्री विधि प्रदान करे, जिसका अनुबंध बताता है कि यह अपनी कक्षा की एक नई वस्तु लौटाएगा; कॉपी कॉन्ट्रैक्टर खुद protectedयह सुनिश्चित करने के लिए होना चाहिए कि इसका उपयोग केवल उन वस्तुओं के निर्माण के लिए किया जाएगा, जिनके सटीक प्रकार का मिलान होने वाली वस्तु से मेल खाता है)।
सुपरकैट

तो अगर मैं आपके सुझाव को सही ढंग से समझूं तो फैक्टरी विधि निजी कॉपी कंस्ट्रक्टर को बुलाएगी? एक उपवर्ग के कॉपी निर्माता कैसे सुनिश्चित करेंगे कि सुपरक्लास खेतों को आरंभीकृत किया गया है? क्या आप एक उदाहरण दे सकते हैं?
एड्रियन कोस्टर

20

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

Cloner cloner = new Cloner();

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

19

Apache commons किसी वस्तु को गहरी क्लोन करने का तेज़ तरीका प्रदान करता है।

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

1
यह केवल उस ऑब्जेक्ट के लिए काम करता है जो Serializable को कार्यान्वित करता है और इसमें सभी क्षेत्रों के लिए भी Serializable को कार्यान्वित करता है।
विजेता

11

XStream ऐसे उदाहरणों में वास्तव में उपयोगी है। यहाँ क्लोनिंग करने का एक सरल कोड है

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

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

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

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


10

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

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
     return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}

9

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

गन्स जो प्रतिबिंब पर आधारित है, ज्यादातर मामलों में काम करेगा, सिवाय इसके कि 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);
}

3
कृपया अपने और हमारे खातिर जावा नामकरण सम्मेलनों का पालन करें।
पैट्रिक बर्गनर

8

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


7

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


2
क्या आप "उपयुक्त डिजाइन प्रथाओं" का वर्णन कर सकते हैं?
फकलप्पन

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));
    }
}


2

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

BeanUtils.cloneBean(obj);

4
यह उथली क्लोनिंग करता है।
पीटर .ály

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 वर्ग को सीरिलज़ेबल इंटरफ़ेस लागू करना होगा


2

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

  <T> T clone(T object, Class<T> clazzType) throws IOException {

    final ObjectMapper objMapper = new ObjectMapper();
    String jsonStr= objMapper.writeValueAsString(object);

    return objMapper.readValue(jsonStr, clazzType);

  }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.