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

en java, il est un peu difficile d'implémenter une fonction de copie d'objets profonds. Quelles mesures prenez-vous pour vous assurer que l'objet original et le cloné ne partagent aucune référence?

247
demandé sur Lazer 2008-09-15 19:39:04

17 réponses

une façon sûre est de sérialiser l'objet, puis de le desé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 que les classes supplantent la sérialisation de sorte que les nouvelles instances soient et non créées, p.ex. pour des singletons. De plus, cela ne fonctionne pas si vos cours ne sont pas sérialisables.

151
répondu Jason Cohen 2008-09-15 15:42:23

quelques personnes ont mentionné l'utilisation ou l'annulation de 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 vous reporter au point 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 sur l'utilisation correcte et le clone supérieur.

les régimes qui reposent sur la sérialisation (XML ou autre) est kludgy.

Il n'y a pas de réponse facile ici. Si vous voulez copier profondément un objet, vous devrez parcourir le graphe d'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 profondément l'objet enfant. Les immuables (par exemple String s) n'ont pas besoin d'être copiés. En aparté, vous devriez favoriser l'immutabilité pour cette raison.

65
répondu 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 copier profondément aura besoin de implement serializable . Si la classe n'est pas Finale ou ne peut pas être modifiée, étendre la classe et implémenter serialisable.

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

Restauration de la classe à partir d'un flux d'octets:

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

vous pouvez faire un clone profond basé sur la sérialisation en utilisant org.apache.commons.lang3.SerializationUtils.clone(T) dans Apache Commons Lang, mais attention-la performance est épouvantable.

En général, il est préférable d'écrire votre propre clone de méthodes pour chaque classe d'un objet dans le graphe d'objets ayant besoin de clonage.

33
répondu user8690 2018-05-09 14:59:52

une façon d'implémenter la copie profonde est d'ajouter des constructeurs de copie à chaque classe associée. Un constructeur de copie prend une instance de 'ceci' comme argument unique et copie toutes les valeurs de celui-ci. Pas mal de travail, mais plutôt simple et sûr.

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

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 les 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 pourriez être en mesure de le faire avec un code de réflexion).

22
répondu Adriaan Koster 2013-08-27 07:50:30

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

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

Vous pouvez utiliser une bibliothèque qui a une API simple, et fonctionne relativement rapide de clonage avec la 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
répondu CorayThan 2017-07-13 07:51:08

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

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

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

une approche très simple et simple est D'utiliser Jackson JSON pour sérialiser un objet Java complexe à JSON et de le relire.

http://wiki.fasterxml.com/JacksonInFiveMinutes

9
répondu Ravi Chinoy 2012-02-07 03:51:31

Use XStream ( http://x-stream.github.io / ). Vous pouvez même contrôler les propriétés que vous pouvez ignorer par des annotations ou en spécifiant explicitement le nom de la propriété de la classe XStream. En outre, vous n'avez pas besoin de mettre en œuvre l'interface clonable.

8
répondu Adi 2016-09-02 11:35:00

la copie profonde ne peut être faite qu'avec le consentement de chaque classe. Si vous avez le contrôle sur la hiérarchie de classe, alors vous pouvez mettre en œuvre l'interface clonable et mettre en œuvre la méthode Clone. Sinon, il est impossible de faire une copie en profondeur en toute sécurité, car l'objet peut aussi partager des ressources autres que des données (p. ex. des connexions à une 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 devrait être évitée via les pratiques de conception appropriées.

6
répondu 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
répondu Eric Leschinski 2015-02-21 00:58:35

pour les objets compliqués et quand la performance n'est pas significative 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 les champs transient ne seront pas copiés et 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
répondu tiboo 2017-10-19 10:30:33

j'ai utilisé Bulldozer pour le clonage des objets java, et c'est génial , Kryo la bibliothèque est une autre excellente alternative.

4
répondu supernova 2014-10-17 02:08:59

Pour Spring des utilisateurs. En utilisant 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
répondu 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 doit implémenter l'interface serilazable

2
répondu Arun 2016-11-11 14:51:53

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

BeanUtils.cloneBean(obj);
1
répondu Alfergon 2016-02-16 10:24:08