73 votes

Copie profonde, copie superficielle, clone

J'ai besoin de précisions sur les différences entre la copie profonde, la copie superficielle et le clone en Java.

110voto

Stephen C Points 255558

Malheureusement, les termes "copie superficielle", "copie profonde" et "clone" sont tous assez mal définis.


Dans le contexte Java, nous devons d'abord faire la distinction entre "copier une valeur" et "copier un objet".

int a = 1;
int b = a;     // copying a value
int[] s = new int[]{42};
int[] t = s;   // copying a value (the object reference for the array above)

StringBuffer sb = new StringBuffer("Hi mom");
               // copying an object.
StringBuffer sb2 = new StringBuffer(sb);

En bref, l'affectation d'une référence à une variable dont le type est un type de référence revient à "copier une valeur" où la valeur est la référence de l'objet. Pour copier un objet, il faut utiliser new soit explicitement, soit sous le capot.


Passons maintenant à la copie "superficielle" et "profonde" des objets. La copie superficielle signifie généralement la copie d'un seul niveau d'un objet, tandis que la copie profonde signifie généralement la copie de plus d'un niveau. Le problème est de déterminer ce que l'on entend par niveau. Considérez ceci :

public class Example {
    public int foo;
    public int[] bar;
    public Example() { };
    public Example(int foo, int[] bar) { this.foo = foo; this.bar = bar; };
}

Example eg1 = new Example(1, new int[]{1, 2});
Example eg2 = ... 

L'interprétation normale est qu'une copie "superficielle" de eg1 serait un nouveau Example objet dont foo est égal à 1 et dont bar se réfère au même tableau que dans l'original ; par ex.

Example eg2 = new Example(eg1.foo, eg1.bar);

L'interprétation normale d'une copie "profonde" de eg1 serait un nouveau Example objet dont foo est égal à 1 et dont bar fait référence à un copie de le tableau original ; par exemple

Example eg2 = new Example(eg1.foo, Arrays.copy(eg1.bar));

(Personnes venant d'un environnement C / C++. pourrait dire qu'une affectation de référence produit une copie superficielle. Cependant, ce n'est pas ce que nous entendons normalement par copie superficielle dans le contexte Java ...)

Deux autres questions/zones d'incertitude existent :

  • Quelle est la profondeur de la profondeur ? Est-ce que ça s'arrête à deux niveaux ? Trois niveaux ? Est-ce que cela signifie le graphe entier des objets connectés ?

  • Qu'en est-il des types de données encapsulées, par exemple une chaîne de caractères ? Une chaîne de caractères n'est en fait pas un simple objet. En fait, c'est un "objet" avec quelques champs scalaires et une référence à un tableau de caractères. Cependant, le tableau de caractères est complètement caché par l'API. Ainsi, lorsque nous parlons de la copie d'une chaîne de caractères, est-il judicieux de l'appeler une copie " superficielle " ou " profonde " ? Ou devrions-nous simplement l'appeler une copie ?


Enfin, le clone. Clone est une méthode qui existe sur toutes les classes (et tableaux) et dont on pense généralement qu'elle produit une copie de l'objet cible. Cependant :

  • La spécification de cette méthode ne dit délibérément pas s'il s'agit d'une copie superficielle ou profonde (en supposant que cette distinction ait un sens).

  • En fait, la spécification n'indique même pas spécifiquement que le clone produit un nouvel objet.

Voici ce que la javadoc dit :

" Crée et renvoie une copie de cet objet. La signification précise de "copie" peut dépendre de la classe de l'objet. L'intention générale est que, pour tout objet x, l'expression x.clone() != x sera vrai, et que l'expression x.clone().getClass() == x.getClass() seront vraies, mais ce ne sont pas des exigences absolues. Bien qu'il soit typiquement le cas que x.clone().equals(x) sera vrai, ce n'est pas une exigence absolue".

Notez que cela revient à dire qu'à un extrême, le clone pourrait soit l'objet cible, et à l'autre extrême le clone pourrait ne pas égalent l'original. Et cela suppose que le clone soit même supporté.

En bref, le mot clone a potentiellement une signification différente pour chaque classe Java.


Certaines personnes affirment (comme @supercat dans les commentaires) que la norme Java clone() est cassée. Mais je pense que la conclusion correcte est que la le concept de clone est cassé dans le contexte de l'OO. A ce jour, il est impossible de développer un modèle unifié de clonage qui soit cohérent et utilisable pour tous les types d'objets.

22voto

Adam Batkin Points 20920

Le terme " clone " est ambigu (bien que la bibliothèque de classes Java comprenne une fonction Clonable ) et peut faire référence à une copie profonde ou à une copie superficielle. Les copies profondes/peu profondes ne sont pas spécifiquement liées à Java mais constituent un concept général relatif à la réalisation d'une copie d'un objet, et font référence à la manière dont les membres d'un objet sont également copiés.

Par exemple, disons que vous avez une classe de personnes :

class Person {
    String name;
    List<String> emailAddresses
}

Comment cloner les objets de cette classe ? Si vous effectuez une copie superficielle, vous pouvez copier name et placer une référence à emailAddresses dans le nouvel objet. Mais si vous avez modifié le contenu de l'objet emailAddresses vous modifieriez la liste dans les deux copies (puisque c'est ainsi que fonctionnent les références d'objets).

Une copie profonde signifierait que vous copiez récursivement chaque membre, vous devriez donc créer un nouveau fichier List pour le nouveau Person puis copier le contenu de l'ancien objet vers le nouveau.

Bien que l'exemple ci-dessus soit trivial, les différences entre les copies profondes et superficielles sont significatives et ont un impact majeur sur toute application, surtout si vous essayez de concevoir une méthode de clonage générique à l'avance, sans savoir comment quelqu'un pourrait l'utiliser plus tard. Il y a des cas où vous avez besoin d'une sémantique profonde ou superficielle, ou d'un hybride où vous copiez profondément certains membres mais pas d'autres.

18voto

Sam Pullara Points 452
  • Copie profonde : Clone cet objet et toutes les références à tous les autres objets qu'il possède.
  • Copie superficielle : Clone cet objet et conserve ses références
  • Objet clone() lève l'exception CloneNotSupportedException : Il n'est pas précisé si cette méthode doit retourner une copie profonde ou superficielle, mais au minimum : o.clone() != o

6voto

Jay Elston Points 1011

A copie superficielle n'est rien de plus qu'une référence à un objet existant. Toute modification apportée à une copie superficielle peut être vue par d'autres références à cet objet. Considérez le code suivant :

ArrayList<Integer> a, b;
a = new ArrayList<Integer>();
a.add(new Integer(1));
b = a;
b.add(new Integer(2));

b est maintenant une "copie superficielle" de a . a et b ont tous deux deux éléments (1 et 2).

En Java, il n'existe pas de définition précise de copie profonde . Il existe une interface qui permet à un développeur de définir ce qu'il faut faire. clone mais cela dépend des caprices du développeur. Copie profonde et clone sous-entend qu'au lieu d'une référence à un objet, vous obtenez un objet totalement séparé et distinct.

Considérez l'exemple de code ci-dessus. Avec une copie profonde (ou clone), les listes a et b seraient distinctes l'une de l'autre. Ainsi, pour une copie profonde ou clone , b.add() n'affecterait pas la liste a . C'est-à-dire, a aurait toujours un seul élément (1), et b aurait deux éléments (1 et 2).

Hélas, ce n'est pas forcément le cas. Il existe une interface ( Clonable ) qui spécifie la manière dont un clone d'une Clonable est créé. La profondeur de la copie dépend de ce que l'équipe qui a défini et implémenté la classe considère comme un clone. Décider de ce qu'il faut faire comme copies profondes ou superficielles d'un objet n'est pas une décision triviale. Il y a des objets pour lesquels faire une copie profonde n'a pas de sens (comme une référence à un singleton), ou n'est pas efficace (comme la copie d'une constante, telle qu'une chaîne).

Voir cet article connexe de StackOverflow ou celui-ci .

3voto

gmhk Points 4151

Consultez cet article

Clone profond

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X