Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - Implement deep and shallow copy of an array

I am trying to understand the concept of shallow vs deep copy in Java. There is a lot of articles and Q&A about this subject, but whenever I try to implement these concepts in a real Java code, everything become unclear to me.

One of the answers on which I base my understanding is in this link, where deep and shallow copying are explained via schemas.

I will show you below my implementation for each case:

  • Shallow copy:

I took for my example the method System.arraycopy() as I read in many articles that it performs a shallow copy (along with the clone method)

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

The result of the execution of the main methods is as follows:

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]

this result is an accordance with the schema of the previous link: Shallow copy

  • Deep copy:

I took for this example the method Arrays.copyOf() as I read in many articles that it performs a deep copy (along with the Arrays.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));
}

which displays:

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]

If we base the deep copy logic on the previous schema, this should be the result: Deep copy

As you may notice, the result of the execution of the main method is different from the logic of the schema above.

Any explanation will be welcome.

like image 232
Strider Avatar asked Jul 28 '16 16:07

Strider


2 Answers

I don't know where you read that copyOf() performs a deep copy, because that is just plain wrong.

Quoting javadoc of Arrays.copyOf(T[] original, int newLength):

For all indices that are valid in both the original array and the copy, the two arrays will contain identical values.

That means it's a shallow copy. To be a deep copy, the values would have to point to different objects, since the referenced object would have to be a copy too.

To perform a deep copy, you have to iterate the array and copy the values. Java can't do that for you, because it doesn't know how to copy the object.

E.g. how would Java know how to copy a NameValue object? clone()? Copy constructor? Serialize+Deserialize? Factory method? Other means?

like image 116
Andreas Avatar answered Oct 10 '22 00:10

Andreas


I am trying to understand the concept of shallow vs deep copy in Java.

In Java you pass around and store references to objects not the objects themselves.
So when you have an NameValue[] array the array does not contain the objects NameValue but references to the objects.
So when you do a shallow copy to NameValue[] array2 it means you are just copying the references from one array to the other. Which effectively means that now both array and array2 refer to exactly the same objects and any change you do from array[2] will be visible from array2[2] (same object).

When you deep copies you copy each object completely to another memory area and you keep a reference to that new object in your new array.
This way the 2 arrays now refer to different objects and any change to array[2] are not visible from array2[2]

Update:
This does not apply to primitives that do store the actual value and not a reference.
So an int[] a when you copy you get a copy of the values (i.e. deep copy in a sense) because a[2] contains the value itself and not the reference to the value.

like image 37
Jim Avatar answered Oct 10 '22 00:10

Jim