Comment faire une copie profonde d'un objet en Java?


En java, il est un peu difficile d'implémenter une fonction deep object copy. Quelles mesures prenez - vous pour vous assurer que l'objet d'origine et le cloné ne partagent aucune référence?

Author: Lazer, 2008-09-15

17 answers

Un moyen sûr est de sérialiser l'objet, puis désérialiser. Cela garantit que tout est une toute nouvelle référence.

Voici un article sur la façon de le faire efficacement.

Mises en garde: Il est possible pour les classes de remplacer la sérialisation de telle sorte que de nouvelles instances soientet non créées, par exemple pour les singletons. De plus, cela ne fonctionne bien sûr pas si vos classes ne sont pas sérialisables.

 151
Author: Jason Cohen, 2008-09-15 15:42:23

Quelques personnes ont mentionné utiliser ou remplacer Object.clone(). Ne pas le faire. Object.clone() a quelques problèmes majeurs, et son utilisation est déconseillée dans la plupart des cas. Veuillez consulter l'article 11, de" Effective Java " par Joshua Bloch pour une réponse complète. Je crois que vous pouvez utiliser en toute sécurité Object.clone() sur les tableaux de type primitif, mais en dehors de cela, vous devez être judicieux pour utiliser et remplacer correctement le clone.

Les schémas qui reposent sur la sérialisation (XML ou autre) sont kludgy.

Il n'y a pas facile réponse ici. Si vous souhaitez copier en profondeur un objet, vous devrez parcourir le graphique de l'objet et copier explicitement chaque objet enfant via le constructeur de copie de l'objet ou une méthode d'usine statique qui à son tour copie en profondeur l'objet enfant. Les immutables (par exemple String s) n'ont pas besoin d'être copiés. En aparté, vous devriez privilégier l'immutabilité pour cette raison.

 65
Author: Julien Chastang, 2017-07-13 07:50:46

Vous pouvez faire une copie profonde avec la sérialisation sans créer de fichiers.

Votre objet que vous souhaitez copie en profondeur devra implement serializable. Si la classe n'est pas finale ou ne peut pas être modifiée, étendez la classe et implémentez serializable.

Convertir votre classe en un flux d'octets:

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

Restaurer votre classe à partir d'un flux d'octets:

ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();
 50
Author: Thargor, 2016-07-07 13:22:57

Vous pouvez effectuer un clone profond basé sur la sérialisation en utilisant org.apache.commons.lang3.SerializationUtils.clone(T) dans Apache Commons Lang, mais attention-les performances sont abyssales.

En général, il est recommandé d'écrire vos propres méthodes de clonage pour chaque classe d'un objet dans le graphe d'objet nécessitant un clonage.

 33
Author: user8690, 2018-05-09 14:59:52

Une façon d'implémenter deep copy consiste à ajouter des constructeurs de copie à chaque classe associée. Un constructeur de copie prend une instance de 'this' comme argument unique et en copie toutes les valeurs. Pas mal de travail, mais assez simple et sûr.

EDIT: notez que vous n'avez pas besoin d'utiliser des méthodes accessor pour lire les champs. Vous pouvez accéder directement à tous les champs car l'instance source est toujours du même type que l'instance avec le constructeur copy. Évident mais pourrait être négliger.

Exemple:

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

Edit: Notez que lorsque vous utilisez des constructeurs de copie, vous devez connaître le type d'exécution de l'objet que vous copiez. Avec l'approche ci-dessus, vous ne pouvez pas facilement copier une liste mixte (vous pourrez peut-être le faire avec du code de réflexion).

 22
Author: Adriaan Koster, 2013-08-27 07:50:30

Apache commons offre un moyen rapide de cloner en profondeur un objet.

My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
 17
Author: TheByeByeMan, 2015-01-28 14:53:24

Vous pouvez utiliser une bibliothèque qui a une API simple et effectue un clonage relativement rapide avec réflexion (devrait être plus rapide que les méthodes de sérialisation).

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
 16
Author: CorayThan, 2017-07-13 07:51:08

XStream est vraiment utile dans de tels cas. Voici un code simple pour faire du clonage

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

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
 11
Author: sankara, 2008-10-23 08:03:08

Une approche très facile et simple consiste à utiliser Jackson JSON pour sérialiser un objet Java complexe en JSON et le relire.

Http://wiki.fasterxml.com/JacksonInFiveMinutes

 9
Author: Ravi Chinoy, 2012-02-07 03:51:31

Utiliser XStream(http://x-stream.github.io/). Vous pouvez même contrôler les propriétés que vous pouvez ignorer via des annotations ou en spécifiant explicitement le nom de la propriété à la classe XStream. De plus, vous n'avez pas besoin d'implémenter une interface clonable.

 8
Author: Adi, 2016-09-02 11:35:00

La copie profonde ne peut être effectuée qu'avec le consentement de chaque classe. Si vous avez le contrôle sur la hiérarchie des classes, vous pouvez implémenter l'interface clonable et implémenter la méthode Clone. Sinon, il est impossible de faire une copie profonde en toute sécurité car l'objet peut également partager des ressources non-données (par exemple, des connexions à la base de données). En général, cependant, la copie profonde est considérée comme une mauvaise pratique dans l'environnement Java et doit être évitée via les pratiques de conception appropriées.

 6
Author: Orion Adrian, 2008-09-15 15:44:32
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));
    }
}
 6
Author: Eric Leschinski, 2015-02-21 00:58:35

Pour les objets compliqués et lorsque les performances ne sont pas significatives, j'utilise une bibliothèque json, comme gson pour sérialiser l'objet json texte, puis désérialiser le texte pour obtenir un nouvel objet.

Gson qui basé sur la réflexion fonctionnera dans la plupart des cas, sauf que transient les champs ne seront pas copiés et les objets avec référence circulaire avec cause 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);

}
 5
Author: tiboo, 2017-10-19 10:30:33

J'ai utilisé Dozerpour cloner des objets java et c'est génial à cela , La bibliothèque Kryo est une autre excellente alternative.

 4
Author: supernova, 2014-10-17 02:08:59

Pour les utilisateurs de Spring Framework. Utilisation de la classe org.springframework.util.SerializationUtils:

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
     return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
 4
Author: Igor Rybak, 2017-07-19 22:15: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);

Ici, votre classe MyPerson et MyAddress doivent implémenter l'interface serilazable

 2
Author: Arun, 2016-11-11 14:51:53

BeanUtils fait un très bon travail de clonage en profondeur des haricots.

BeanUtils.cloneBean(obj);
 1
Author: Alfergon, 2016-02-16 10:24:08