Java-implémenter la copie profonde et superficielle d'un tableau

j'essaie de comprendre le concept de copie superficielle vs profonde En Java. Il y a beaucoup d'articles et de questions et Réponses sur ce sujet, mais chaque fois que j'essaie d'implémenter ces concepts dans un vrai code Java, tout devient flou pour moi.

L'une des réponses sur lesquelles je fonde mon interprétation est dans ce lien , où la copie profonde et superficielle est expliquée par des schémas.

je vais vous montrer ci-dessous mon implémentation pour chaque case:

  • "Copie peu profonde:

j'ai pris pour mon exemple la méthode système.arraycopy() comme je l'ai lu dans plusieurs articles qu'il effectue une copie superficielle (avec le clone méthode

public class Test {

    public static void main(String[] args) {
        NameValue[] instance1 = {
                new NameValue("name1", 1),
                new NameValue("name2", 2),
                new NameValue("name3", 3),
        };
        NameValue[] instance2 = new NameValue[instance1.length];

        // Print initial state
        System.out.println("Arrays before shallow copy:");
        System.out.println("Instance 1: " + Arrays.toString(instance1));
        System.out.println("Instance 2: " + Arrays.toString(instance2));

        // Perform shallow copy
        System.arraycopy(instance1, 0, instance2, 0, 3);

        // Change instance 1
        for (int i = 0; i < 3; i++) {
            instance1[i].change();
        }

        // Print final state
        System.out.println("Arrays after shallow copy:");
        System.out.println("Instance 1: " + Arrays.toString(instance1));
        System.out.println("Instance 2: " + Arrays.toString(instance2));
    }

    private static class NameValue {
        private String name;
        private int value;

        public NameValue(String name, int value) {
            super();
            this.name = name;
            this.value = value;
        }

        public void change() {
            this.name = this.name + "-bis";
            this.value = this.value + 1;
        }

        @Override
        public String toString() {
            return this.name + ": " + this.value;
        }
    }
}

Le résultat de l'exécution des principales méthodes est comme suit:

Arrays before shallow copy:
Instance 1: [name1: 1, name2: 2, name3: 3]
Instance 2: [null, null, null]
Arrays after shallow copy:
Instance 1: [name1-bis: 2, name2-bis: 3, name3-bis: 4]
Instance 2: [name1-bis: 2, name2-bis: 3, name3-bis: 4]

ce résultat est un conformément au schéma du lien précédent: Shallow copy

  • copie en Profondeur:

j'ai pris pour cet exemple la méthode tableaux.copyOf () comme je l'ai lu dans de nombreux articles qu'il effectue une copie profonde (avec les tableaux .copyOfRange method)

public static void main(String[] args) {
    NameValue[] instance1 = {
            new NameValue("name1", 1),
            new NameValue("name2", 2),
            new NameValue("name3", 3),
    };
    NameValue[] instance2 = new NameValue[instance1.length];

    // Print initial state
    System.out.println("Arrays before deep copy:");
    System.out.println("Instance 1: " + Arrays.toString(instance1));
    System.out.println("Instance 2: " + Arrays.toString(instance2));

    // Perform deep copy
    instance2 = Arrays.copyOf(instance1, 3);

    // Change instance 1
    for (int i = 0; i < 3; i++) {
        instance2[i].change();
    }

    // Print final state
    System.out.println("Arrays after deep copy:");
    System.out.println("Instance 1: " + Arrays.toString(instance1));
    System.out.println("Instance 2: " + Arrays.toString(instance2));
}

qui affiche:

Arrays before deep copy:
Instance 1: [name1: 1, name2: 2, name3: 3]
Instance 2: [null, null, null]
Arrays after deep copy:
Instance 1: [name1-bis: 2, name2-bis: 3, name3-bis: 4]
Instance 2: [name1-bis: 2, name2-bis: 3, name3-bis: 4]

si nous basons la logique de la copie profonde sur le schéma précédent, cela devrait être le résultat: Deep copy

Comme vous pouvez le remarquer, le résultat de l'exécution de la méthode main est différente de la logique du schéma ci-dessus.

Toute explication sera la bienvenue.

8
demandé sur Community 2016-07-28 19:04:30

3 réponses

j'essaie de comprendre le concept de copie superficielle vs profonde En Java.

en Java, vous faites circuler et stockez des références à des objets et non aux objets eux-mêmes.

Ainsi, lorsque vous avez un NameValue[] array le tableau ne contient pas les objets NameValue mais des références aux objets.

Donc quand vous faites une copie superficielle à NameValue[] array2 cela signifie que vous copiez juste les références à partir d'un tableau à l'autre. Ce qui signifie effectivement que maintenant les deux array et array2 se réfèrent exactement aux mêmes objets et tout changement que vous faites de array[2] sera visible de array2[2] (même objet).

lorsque vous copiez en profondeur vous copiez chaque objet complètement dans une autre zone de mémoire et vous gardez une référence à ce nouvel objet dans votre nouveau tableau.

De cette façon, les 2 tableaux se réfèrent maintenant à des objets différents et à tout changement array[2] ne sont pas visibles de array2[2]

mise à jour:

Cela ne s'applique pas aux primitifs qui stockent la valeur réelle et non une référence.

Ainsi, un int[] a quand vous copiez vous obtenez une copie des valeurs (i.e. copie profonde dans un sens) parce que a[2] contient la valeur elle-même et non la référence à la valeur.

4
répondu Jim 2016-07-28 16:53:15

Je ne sais pas où vous avez lu que copyOf() effectue une copie profonde, parce que c'est tout simplement faux.

citant javadoc de Arrays.copyOf(T[] original, int newLength) :

pour tous les indices qui sont valides à la fois dans le tableau original et dans la copie, les deux tableaux contiennent des valeurs identiques .

ça veut dire que c'est une copie superficielle. Pour être une copie profonde, les valeurs devraient indiquer les différents objets, puisque l'objet référencé devrait être une copie.

pour effectuer une copie profonde, vous doivent itérer le tableau et copier les valeurs. Java ne peut pas faire cela pour vous, parce qu'il ne sait pas comment copier l'objet.

E. G. comment Java pourrait-il savoir copier un objet NameValue ? clone() ? Copie du constructeur? Sérialiser+Désérialiser? Méthode de fabrique? D'autres moyens?

5
répondu Andreas 2016-07-28 16:23:22

je pense qu'il y a un petit malentendu que Arrays.copyOf() produit une copie profonde.

Arrays.copyOf() fait un nouveau tableau qui contient de vieilles références à des objets qui ne sont pas copiés et comme le lien que j'ai ajouté explique dans le cas de tableaux imbriqués ils ne seront pas copiés et donc il ne peut pas être considéré comme une deep copie mais une copie superficielle.

voir ce pour plus de renseignements.

1
répondu Gherbi Hicham 2017-05-23 12:01:05