Hoe maak je een diepe kopie van een object?

Het is een beetje moeilijk om een functie voor het kopiëren van diepe objecten te implementeren. Welke stappen onderneemt u om ervoor te zorgen dat het originele object en het gekloonde object geen referentie delen?


Antwoord 1, autoriteit 100%

Een veilige manier is om het object te serialiseren en vervolgens te deserialiseren. Dit zorgt ervoor dat alles een gloednieuwe referentie is.

Hier is een artikelover hoe om dit efficiënt te doen.

Voorbehoud: het is mogelijk voor klassen om serialisatie te overschrijven, zodat nieuwe instanties nietworden gemaakt, b.v. voor eenlingen. Dit werkt natuurlijk ook niet als je lessen niet serialiseerbaar zijn.


Antwoord 2, autoriteit 46%

Een paar mensen hebben gezegd dat ze Object.clone()gebruiken of negeren. Doe het niet. Object.clone()heeft enkele grote problemen en het gebruik ervan wordt in de meeste gevallen afgeraden. Zie item 11 van “Effective Java” door Joshua Bloch voor een volledig antwoord. Ik geloof dat je Object.clone()veilig kunt gebruiken op primitieve type arrays, maar afgezien daarvan moet je oordeelkundig zijn over het correct gebruiken en overschrijven van kloon.

De schema’s die afhankelijk zijn van serialisatie (XML of anderszins) zijn onhandig.

Er is hier geen eenvoudig antwoord. Als u een object diep wilt kopiëren, moet u de objectgrafiek doorlopen en elk kindobject expliciet kopiëren via de kopieerconstructor van het object of een statische fabrieksmethode die op zijn beurt het onderliggende object diep kopieert. Onveranderlijke waarden (bijv. Strings) hoeven niet te worden gekopieerd. Even terzijde, je zou om deze reden de voorkeur moeten geven aan onveranderlijkheid.


Antwoord 3, autoriteit 34%

Je kunt een diepe kopie maken met serialisatie zonder bestanden te maken.

Uw object dat u wilt diee-kopie is nodig om implement serializablete implementeren. Als de klas niet definitief is of niet kan worden gewijzigd, breidt u de klasse uit en implementeer Serializable.

Converteer uw klasse naar een stroom van bytes:

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

Herstel uw klas van een stroom van bytes:

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

Antwoord 4, Autoriteit 25%

U kunt een serialisatie-gebaseerde diepe kloon gebruiken met org.apache.commons.lang3.SerializationUtils.clone(T)in Apache Commons Lang, maar wees voorzichtig – de uitvoering is abysmaal.

In het algemeen is het de beste praktijken om uw eigen kloonmethoden te schrijven voor elke klasse van een object in het objectgrafiek dat klonering nodig heeft.


Antwoord 5, Autoriteit 16%

One Way to Implement Deep Copy is om kopieerconstructeurs aan elke bijbehorende les toe te voegen. Een kopieerconstructeur neemt een exemplaar van ‘dit’ als het enige argument en kopieert alle waarden eruit. Vrij werk, maar behoorlijk eenvoudig en veilig.

EDIT: Merk op dat u geen toegangswerkwijzen hoeft te gebruiken om velden te lezen. U hebt rechtstreeks toegang tot alle velden omdat de broninstantie altijd van hetzelfde type is als het exemplaar met de Copy Constructor. Voor de hand liggend maar kan over het hoofd worden gezien.

Voorbeeld:

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

Bewerken: Houd er rekening mee dat wanneer u kopieerconstructors gebruikt, u het runtime-type moet weten van het object dat u kopieert. Met de bovenstaande aanpak kun je niet gemakkelijk een gemengde lijst kopiëren (je kunt het misschien doen met een reflectiecode).


Antwoord 6, autoriteit 12%

Je kunt een bibliotheek gebruikendie een eenvoudige API heeft en relatief snel klonen met reflectie (moet sneller zijn dan serialisatiemethoden).

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

Antwoord 7, autoriteit 11%

Apache commons biedt een snelle manier om een object diep te klonen.

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

Antwoord 8, autoriteit 8%

Voor gebruikers van Spring Framework. Class org.springframework.util.SerializationUtilsgebruiken:

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

Antwoord 9, autoriteit 7%

Voor gecompliceerde objecten en wanneer de prestaties niet significant zijn, gebruik ik een json-bibliotheek, zoals gson
om het object te serialiseren naar json-tekst en vervolgens de tekst te deserialiseren om een nieuw object te krijgen.

gson die gebaseerd is op reflectie zal in de meeste gevallen werken, behalve dat transientvelden niet worden gekopieerd en objecten met kringverwijzing met oorzaak 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);
}

Antwoord 10, autoriteit 6%

XStream is in dergelijke gevallen erg handig. Hier is een eenvoudige code om te klonen

private static final XStream XSTREAM = new XStream();
...
Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));

Antwoord 11, autoriteit 6%

Een zeer gemakkelijke en eenvoudige benadering is om Jackson JSON te gebruiken om complexe Java-objecten naar JSON te serialiseren en terug te lezen.

Van https://github.com /FasterXML/jackson-databind/#5-minute-tutorial-streaming-parser-generator:

JsonFactory f = mapper.getFactory(); // may alternatively construct directly too
// First: write simple JSON output
File jsonFile = new File("test.json");
JsonGenerator g = f.createGenerator(jsonFile);
// write JSON: { "message" : "Hello world!" }
g.writeStartObject();
g.writeStringField("message", "Hello world!");
g.writeEndObject();
g.close();
// Second: read file back
JsonParser p = f.createParser(jsonFile);
JsonToken t = p.nextToken(); // Should be JsonToken.START_OBJECT
t = p.nextToken(); // JsonToken.FIELD_NAME
if ((t != JsonToken.FIELD_NAME) || !"message".equals(p.getCurrentName())) {
   // handle error
}
t = p.nextToken();
if (t != JsonToken.VALUE_STRING) {
   // similarly
}
String msg = p.getText();
System.out.printf("My message to you is: %s!\n", msg);
p.close();

Antwoord 12, autoriteit 4%

Gebruik XStream(http://x-stream.github.io/). U kunt zelfs bepalen welke eigenschappen u kunt negeren door middel van annotaties of door de eigenschapsnaam expliciet op te geven voor de XStream-klasse. Bovendien hoeft u geen kloonbare interface te implementeren.


Antwoord 13, autoriteit 4%

Diep kopiëren kan alleen met toestemming van elke klas. Als u controle hebt over de klassenhiërarchie, kunt u de kloonbare interface implementeren en de Clone-methode implementeren. Anders is het onmogelijk om veilig een diepe kopie te maken, omdat het object mogelijk ook niet-gegevensbronnen deelt (bijvoorbeeld databaseverbindingen). Over het algemeen wordt diep kopiëren echter als een slechte gewoonte beschouwd in de Java-omgeving en moet het worden vermeden via de juiste ontwerppraktijken.


Antwoord 14, autoriteit 3%

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

Antwoord 15, autoriteit 3%

Ik heb Dozergebruikt voor het klonen van Java-objecten en daar is het geweldig in, Kryobibliotheek is een ander geweldig alternatief.


Antwoord 16, autoriteit 3%

Jackson gebruiken om het object te serialiseren en te deserialiseren. Deze implementatie vereist niet dat het object de klasse Serializable implementeert.

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

Antwoord 17

BeanUtilsdoet echt goed werk bij het diep klonen van bonen.

BeanUtils.cloneBean(obj);

Antwoord 18

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

Hier moeten uw MyPerson en MyAddress Class de serilazable interface uitvoeren


Antwoord 19

Hier is een generieke diepe cloning-methode met behulp van objectserialisatie en deserialization met byte array-streams (om te voorkomen dat u een bestand kunt schrijven).

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepClone(T t) {
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);) {
        oos.writeObject(t);
        byte[] bytes = baos.toByteArray();
        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
            return (T) ois.readObject();
        }
    } catch (IOException | ClassNotFoundException e) {
        throw new RuntimeException(e);
    }
}

Antwoord 20

Hier is een eenvoudig voorbeeld van hoe je een object diep kunt klonen:
Implementeer eerst serialiseerbaar

public class CSVTable implements Serializable{
    Table<Integer, Integer, String> table; 
    public CSVTable() {
        this.table = HashBasedTable.create();
    }
    public CSVTable deepClone() {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            return (CSVTable) ois.readObject();
        } catch (IOException e) {
            return null;
        } catch (ClassNotFoundException e) {
            return null;
        }
    }
}

En dan

CSVTable table = new CSVTable();
CSVTable tempTable = table.deepClone();

zo krijg je de kloon.

Other episodes