web-dev-qa-db-fra.com

En Java, qu’est-ce qu’une copie superficielle?

Java.util.Calendar.clone () renvoie "... un nouveau calendrier avec les mêmes propriétés" et renvoie "une copie superficielle de ce calendrier".

Il ne semble pas que ce soit une copie superficielle répondant à la réponse ici sur SO. Cette question est étiquetée language-agnostic, Java ne semble pas suivre la définition agnostique du langage. En parcourant le code, je remarque que la structure et les éléments sont copiés dans ce nouvel objet, plus que la structure agnostique du langage.

En Java, qu’est-ce qu’une copie superficielle?

En quoi diffère-t-il d'une copie profonde Java (si celle-ci existe)?

47
Will

Une copie superficielle ne fait que copier les valeurs des références de la classe. Une copie en profondeur copie les valeurs. donné:

class Foo {
  private Bar myBar;
  ...
  public Foo shallowCopy() {
    Foo newFoo = new Foo();
    newFoo.myBar = myBar;
    return newFoo;
  }

  public Foo deepCopy() {
    Foo newFoo = new Foo();
    newFoo.myBar = myBar.clone(); //or new Bar(myBar) or myBar.deepCopy or ...
    return newFoo;
  }
}

Foo myFoo = new Foo();  
Foo sFoo = myFoo.shallowCopy();  
Foo dFoo = myFoo.deepCopy();  

myFoo.myBar == sFoo.myBar => true  
myFoo.myBar.equals(sFoo.myBar) => true  
myFoo.myBar == dFoo.myBar => **false**  
myFoo.myBar.equals(dFoo.myBar) => true  

Dans ce cas, la copie superficielle a la même référence (==) et la copie complète n'a qu'une référence équivalente (.equals()). 

Si une modification est apportée à la valeur d'une référence copiée de manière superficielle, la copie reflète cette modification car elle partage la même référence. Si une modification est apportée à la valeur d'une référence profondément copiée, la copie ne reflète pas cette modification car elle ne partage pas la même référence.

C-isme

int a = 10; //init
int& b = a; //shallow - copies REFERENCE
int c = a;  //deep - copies VALUE
++a;

Résultat:

a is 11  
*b is 11  
c is 10
70
KitsuneYMG

Une copie superficielle est juste un ensemble de pointeurs vers les mêmes emplacements de mémoire. En réalité, il ne crée pas de copie réelle, ce qui réduit l'utilisation de la mémoire.

Dans le cas d'une copie profonde, une copie exacte du segment de mémoire est créée et les pointeurs sont définis sur les nouveaux emplacements de mémoire. Donc théoriquement, la consommation de mémoire devrait être deux fois dans ce cas.

11

Une copie superficielle est une copie du pointeur de référence sur l'objet, alors qu'une copie complète est une copie de l'objet lui-même. En Java, les objets sont conservés à l’arrière-plan. Ce sont les pointeurs avec lesquels vous interagissez normalement. Les noms de variables pointent vers l'espace mémoire de l'objet. Une copie superficielle est faite lorsque vous définissez une variable égale à une autre, comme ceci: 

Object B = A;

Une copie en profondeur peut être réalisée en récupérant les propriétés de l'objet A et en les plaçant dans un nouvel objet B.

Object B = new Object(A.getProperty1(), A.getProperty2()...);

Cela affecte le comportement du programme en ce que si vous effectuez une copie superficielle et effectuez une tâche dessus, cela affecte toutes les copies superficielles de l'objet. Si vous modifiez une copie complète, seule cette copie est affectée. J'espère que cela est suffisamment détaillé pour vous.

5
mnuzzo

Le document 1.6 docs Calendar.clone en tant que "crée et renvoie une copie de cet objet". Une copie superficielle littérale spécifiée par Object.clone n'aurait aucun sens. Java utilise le terme "copie superficielle" dans un sens assez typique.

3

Cela semble être une erreur dans la documentation. Je ne vois pas comment la méthode Calendar.clone d'Android répond à la définition typique (en Java ou autre) d'une "copie superficielle".

2
jsight

Où trouvez-vous cette documentation?

Les documents officiels Java 6 sur Java.Sun.com ont simplement Calendar.clone () retournant une copie de l'objet. Aucune mention de peu profonde.

Plus généralement, une copie superficielle en Java est une copie dans laquelle vous obtenez une nouvelle référence à un objet, mais le nouvel objet contient (directement ou indirectement) des références à des données dans l'original.

Par exemple:

class MyClass{
  private List<Integer> innerList;

  public MyClass(List<Integer> list) { innerList = list; }

  //Some code...

  public Object clone(){
    return new MyClass(innerList);
  }
}

retourne une copie superficielle dans son clone ().

1
Kevin Montrose

Tout d'abord, la Javadoc de ArrayList est un peu fausse si nous parlons de tableaux unidimensionnels, car elle utilise la méthode copyOf dans Arrays. Donc, clone () restitue une copie unidimensionnelle, au moins depuis la version 1.5 (je n'ai pas testé plus loin)! Voilà donc ce que "peu profond" signifie en Java: unidimensionnel

Vous pouvez en lire plus ici: http://www.javapractices.com/topic/TopicAction.do?Id=3 . Donc, clone () n’est pas une copie superficielle! Si vous voulez une copie peu profonde réelle d'un tableau à une dimension, il suffit de le référencer:

Array a = new Array();
Array b = a;                    //a is only a shallow copy, Nice for synchronisation

Les tableaux en Java sont délicats, également parce que Java transmet valeur par valeur, mais les valeurs des tableaux ne sont que leurs pointeurs! D'autre part, cela nous permet de synchroniser les objets, ce qui est une bonne chose. Il y a tout de même des problèmes si vous utilisez des tableaux dans des tableaux (ou ArrayLists), car un clone () du tableau conteneur (ou ArrayList) ne copie pas leurs valeurs, mais leurs références! Donc, vous ne devriez simplement pas placer de tableaux dans un tableau, vous ne devriez traiter que des objets dans un tableau!

Et Javadoc est parfois difficile à comprendre, alors essayez-le ...

S'amuser!

0
mark

Dans une copie superficielle, l'objet clone possède une copie des valeurs primitives mais les références d'objet font référence aux mêmes objets que la copie d'origine . Les copies peu profondes ont un inconvénient important. L'objet cloné et la copie d'origine font référence au même objet d'adresse. Toute modification apportée par l'objet cloné dans l'objet adresse sera également reflétée dans la copie d'origine, ce qui constitue un comportement indésirable. Ce que nous voulions vraiment, c’est deux copies distinctes de l’objet utilisateur. La copie en profondeur vient à notre secours pour ce genre de situation.

La copie en profondeur ne copie pas uniquement les valeurs primitives, elle crée également des copies des références d'objet. 

Vous pouvez consulter un exemple de travail sur ce sujet ici: https://codingninjaonline.com/2017/11/09/deep-vs-shallow-copy/

0
pndey

Une copie superficielle copie simplement la référence de l'objet dans la référence cible. Il ne crée pas de nouvel objet sur le tas. Par défaut, Java effectue un clonage superficiel à l'aide de la fonction clone ().

Pour obtenir un nouvel objet sur le tas, il faut effectuer un clonage en profondeur qui peut être implémenté par Sérialisation et Désérialisation.

0
Akanksha