1.4-Software-Development-Principles

Copy vs. Clone

Introduction

Keeping your data secure and correct is a vital part of your program.That is why we use private variables and constructors to ensure a correct state. A second requirements is making that data available to others, but that may endanger our first requirement. Who knows what those “Evil Others” are going to do with our precious data?

Rather than hoarding the data for ourselves only we’re going to workon the idea of given others only access to a copy of our data.

Copying

Here are two lists of data. Well, actually they are two variables that reference the same list. Both variables point to the same array in heap memory.

int[] numbers = { 2, 3, 4, 5};
int[] numbersCopy = numbers;

So if I were to do something like:

numbersCopy[2] = 0;

Then both variables will point to a reference whose element 2 now has a value of 0.

This is why you should never write a method like this, before thinking very carefully about it:

public int[] getList() {
	return myPreciousss;
}

Cloning

In order to avoid these problems we create a new separate copy and return only that version. This is called cloning:

  int[] numbers = { 2, 3, 4, 5};
  int[] numbersClone = (int[])numbers.clone();

Initially both versions contain the same data, but because they point to two separate references in the heap. The clone is free to be changed without affecting the original.

Deep clone

In the above example we used an array with integer values. So the only reference type is the array itself, but what happens if we store more complex objects in the array:

class Person {
	private String name;
	
	public Person(String name) {
		this.name = name;
    }
	
	public String getName() {
		return this.name;
    }
    
	public void setName(String name) {
		this.name = name;
    }
}

Now we can will an array with Persons:

Person[] persons = new Person[3];
persons[0] = new Person("Dick");
persons[1] = new Person("Jan Jaap");
persons[2] = new Person("Peter"); 

Even though we may create a clone of this array:

Person[] personsClone = (Person[])persons.clone();

Some nefarious character may still do the following:

personsClone[0] = null; // Only affects the cloned array.
personsClone[1].setName("Marcel"); // Changes the name of the same Person reference.

The cloned array of persons is a separate array that contains references to the same Person objects in the heap. This means that the array can be changed independently, but the references affect the very same instances.

To solve this you need to also clone the Persons in the array in order to create two entirely separate data structures in memory:

public class Person implements Cloneable {
	// ...
	@Override
	public Person clone() {
		return new Person(getName());
	}
}

First we implement the Cloneable interface and create a way to make an entirely different instance of the current Person.

Creating a “deep clone” of the Persons array means that each reference in the array is cloned as well. (And so forth al through the datastructure.)

Person[] deepClone = new Person[original.length];
for (int i = 0; i < deepClone.length; i++) {
    deepClone[i] = original[i].clone();
}

Conclusion

Creating an entire copy of your data structure means that all data is now stored twicein memory. This is the sacrifice you need to make in order to provide untrusted externalparties (are there other kinds?) with a copy of your data.

No matter what they do to their copy (clone) of the data, your original data remains safe and intact.