Skip to content
Part IA Michaelmas Term

Shallow versus Deep Copies

Shallow vs deep copy comparison

Definitions

A shallow copy duplicates an object’s fields, but for any reference-type field, it copies only the reference — so the original and the copy still share the same nested objects.

A deep copy recursively copies every nested reachable object too, so the copy shares nothing mutable with the original.

The Car and Tyre Problem

This is a classic Tripos bug (2010 Q7 — cloning cars with shared tyres):

class Tyre {
    int pressure;
    Tyre(int p) { this.pressure = p; }
}

class Car {
    Tyre[] tyres = new Tyre[4];

    Car() {
        for (int i = 0; i < 4; i++) tyres[i] = new Tyre(30);
    }

    Car shallowCopy() {
        Car c = new Car();
        c.tyres = this.tyres;   // copies the REFERENCE to the array
        return c;
    }
}

Car original = new Car();
Car copy = original.shallowCopy();
copy.tyres[0].pressure = 40;
System.out.println(original.tyres[0].pressure);  // 40 — the original changed!

With a shallow copy, both original.tyres and copy.tyres point to the same Tyre[] array. Inflating one car’s tyres “magically” inflates the other’s. The two Car objects are distinct, but they share the same Tyre objects — and mutation through either reference affects both.

Deep Copy

A deep copy must recursively clone every mutable field:

Car deepCopy() {
    Car c = new Car();
    for (int i = 0; i < 4; i++) {
        c.tyres[i] = new Tyre(this.tyres[i].pressure);
    }
    return c;
}

Now each Car has its own Tyre[] array containing its own Tyre objects. Mutations to one car’s tyres do not affect the other.

Immutability and Sharing

For immutable objects (String, Integer, LocalDate, etc.), you do not need to deep-copy because they cannot be mutated — sharing is safe:

class Person {
    String name;           // immutable — sharing is fine
    LocalDate birthDate;   // immutable — sharing is fine
    Address homeAddress;   // mutable — must deep-copy!
    List<String> tags;     // List is mutable — must deep-copy!
}

Deep-copying an immutable field is wasted work and memory. A reference to the same String is perfectly safe because no code can change the string’s contents.

Purity versus Practicality

A fully deep copy recursively copies all reachable objects, producing a completely independent clone. In theory this is clean; in practice it is often impractical:

  • Objects may reference shared caches or singletons that should not be duplicated.
  • An object graph may have cycles, requiring cycle detection during copying.
  • Copying every field may be expensive and unnecessary if the copied fields are not going to be mutated.

In practice, hybrid approaches are common — deep-copy mutable fields, share immutable ones.

Summary Table

PropertyShallow CopyDeep Copy
PrimitivesCopied by valueCopied by value
Immutable references (String, Integer, …)Reference copied (safe to share)Reference copied (safe to share)
Mutable references (arrays, collections, custom objects)Reference copied (shared — dangerous)New object created recursively
IndependencePartial — shared mutable stateFull — no shared mutable state
PerformanceFastSlower, more memory