What is the difference between a deep copy and a shallow copy?
Categories:
Deep Copy vs. Shallow Copy: Understanding Data Duplication

Explore the fundamental differences between deep and shallow copies, their implications for data integrity, and when to use each in programming.
In programming, copying data structures is a common operation. However, the way you copy can significantly impact how your program behaves, especially when dealing with complex objects that contain references to other objects. This article delves into the concepts of shallow copy and deep copy, explaining their mechanisms, use cases, and potential pitfalls.
What is a Shallow Copy?
A shallow copy creates a new object, but it does not create copies of the nested objects or referenced data within the original object. Instead, it copies the references to those nested objects. This means that both the original and the shallow-copied object will point to the same underlying nested data. If you modify a nested object through either the original or the copied object, the changes will be reflected in both.
flowchart LR subgraph Original Object A[Original Parent] --> B(Nested Object 1) A --> C(Nested Object 2) end subgraph Shallow Copy D[Shallow Copy Parent] --> B D --> C end style Original Object fill:#f9f,stroke:#333,stroke-width:2px style Shallow Copy fill:#ccf,stroke:#333,stroke-width:2px B:::shared C:::shared classDef shared fill:#afa,stroke:#333,stroke-width:2px linkStyle 0,1,2,3 stroke-width:2px,fill:none,stroke:black;
Shallow Copy: Both parent objects reference the same nested objects.
import copy
original_list = [1, [2, 3], 4]
shallow_copied_list = list(original_list) # Or original_list[:]
print(f"Original: {original_list}")
print(f"Shallow Copy: {shallow_copied_list}")
# Modify a nested object in the shallow copy
shallow_copied_list[1][0] = 99
print(f"\nAfter modification:")
print(f"Original: {original_list}") # Original is also changed!
print(f"Shallow Copy: {shallow_copied_list}")
Python example demonstrating a shallow copy and its effect on nested mutable objects.
What is a Deep Copy?
A deep copy, in contrast, creates a completely independent duplicate of the original object. This means it not only copies the top-level object but also recursively copies all nested objects and their contents. As a result, the original and the deep-copied object are entirely separate entities. Modifications to one will not affect the other.
flowchart LR subgraph Original Object A[Original Parent] --> B(Nested Object 1) A --> C(Nested Object 2) end subgraph Deep Copy D[Deep Copy Parent] --> E(Nested Object 1 Copy) D --> F(Nested Object 2 Copy) end style Original Object fill:#f9f,stroke:#333,stroke-width:2px style Deep Copy fill:#ccf,stroke:#333,stroke-width:2px B:::original C:::original E:::copy F:::copy classDef original fill:#afa,stroke:#333,stroke-width:2px classDef copy fill:#add8e6,stroke:#333,stroke-width:2px linkStyle 0,1,2,3 stroke-width:2px,fill:none,stroke:black;
Deep Copy: Both parent objects and their nested objects are independent.
import copy
original_list = [1, [2, 3], 4]
deep_copied_list = copy.deepcopy(original_list)
print(f"Original: {original_list}")
print(f"Deep Copy: {deep_copied_list}")
# Modify a nested object in the deep copy
deep_copied_list[1][0] = 99
print(f"\nAfter modification:")
print(f"Original: {original_list}") # Original remains unchanged
print(f"Deep Copy: {deep_copied_list}")
Python example demonstrating a deep copy and its independence from the original.
When to Use Which?
The choice between a shallow and a deep copy depends entirely on your specific requirements and the nature of the data you are working with.
Shallow Copy: Use a shallow copy when your object contains only immutable data types (like numbers, strings, tuples in Python) or when you intentionally want changes to nested mutable objects to be reflected across all copies. It's also suitable for performance-critical scenarios where a full duplication is unnecessary.
Deep Copy: Opt for a deep copy when your object contains mutable nested objects and you need complete independence between the original and the copy. This is crucial for maintaining data integrity and preventing unexpected side effects, especially in scenarios like undo/redo functionalities, state management, or when passing objects to functions that might modify them.
JavaScript
const original = { a: 1, b: { c: 2 } };
// Shallow Copy (using spread operator or Object.assign) const shallowCopy = { ...original }; shallowCopy.a = 10; shallowCopy.b.c = 20; // Modifies original.b.c as well
console.log('Original (shallow):', original); // { a: 1, b: { c: 20 } } console.log('Shallow Copy:', shallowCopy); // { a: 10, b: { c: 20 } }
// Deep Copy (using JSON.parse(JSON.stringify()) - has limitations) const original2 = { a: 1, b: { c: 2 }, d: new Date() }; const deepCopy = JSON.parse(JSON.stringify(original2)); deepCopy.a = 10; deepCopy.b.c = 20;
console.log('\nOriginal (deep):', original2); // { a: 1, b: { c: 2 }, d: Date object } console.log('Deep Copy:', deepCopy); // { a: 10, b: { c: 20 }, d: Date string }
// For robust deep copy in JS, consider libraries like Lodash's _.cloneDeep
Java
class Address { String street; Address(String street) { this.street = street; } @Override public String toString() { return "Address{" + "street='" + street + ''' + '}'; } }
class Person implements Cloneable { String name; Address address;
Person(String name, Address address) {
this.name = name;
this.address = address;
}
// Shallow Copy (default Object.clone() behavior)
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
// Deep Copy (manual implementation)
public Person deepClone() throws CloneNotSupportedException {
Person clonedPerson = (Person) super.clone();
clonedPerson.address = new Address(this.address.street); // Deep copy the Address object
return clonedPerson;
}
@Override public String toString() { return "Person{" + "name='" + name + '\'' + ", address=" + address + '}'; }
}
public class CopyExample { public static void main(String[] args) throws CloneNotSupportedException { Address originalAddress = new Address("123 Main St"); Person originalPerson = new Person("Alice", originalAddress);
// Shallow Copy
Person shallowCopyPerson = (Person) originalPerson.clone();
shallowCopyPerson.name = "Bob";
shallowCopyPerson.address.street = "456 Oak Ave"; // Modifies originalAddress as well
System.out.println("Original Person (shallow): " + originalPerson);
System.out.println("Shallow Copy Person: " + shallowCopyPerson);
// Deep Copy
Address originalAddress2 = new Address("789 Pine Ln");
Person originalPerson2 = new Person("Charlie", originalAddress2);
Person deepCopyPerson = originalPerson2.deepClone();
deepCopyPerson.name = "David";
deepCopyPerson.address.street = "101 Elm Rd"; // Only modifies deepCopyPerson's address
System.out.println("\nOriginal Person (deep): " + originalPerson2);
System.out.println("Deep Copy Person: " + deepCopyPerson);
}
}
C#
using System;
public class Address { public string Street { get; set; } public Address(string street) { Street = street; } public override string ToString() => $"Address{{Street='{Street}'}}"; }
public class Person { public string Name { get; set; } public Address HomeAddress { get; set; }
public Person(string name, Address address)
{
Name = name;
HomeAddress = address;
}
// Shallow Copy (MemberwiseClone)
public Person ShallowCopy()
{
return (Person)this.MemberwiseClone();
}
// Deep Copy (manual implementation)
public Person DeepCopy()
{
Person deepClonedPerson = (Person)this.MemberwiseClone();
deepClonedPerson.HomeAddress = new Address(this.HomeAddress.Street); // Deep copy the Address object
return deepClonedPerson;
}
public override string ToString() => $"Person{{Name='{Name}', Address={HomeAddress}}}";
}
public class CopyExample { public static void Main(string[] args) { Address originalAddress = new Address("123 Main St"); Person originalPerson = new Person("Alice", originalAddress);
// Shallow Copy
Person shallowCopyPerson = originalPerson.ShallowCopy();
shallowCopyPerson.Name = "Bob";
shallowCopyPerson.HomeAddress.Street = "456 Oak Ave"; // Modifies originalAddress as well
Console.WriteLine($"Original Person (shallow): {originalPerson}");
Console.WriteLine($"Shallow Copy Person: {shallowCopyPerson}");
// Deep Copy
Address originalAddress2 = new Address("789 Pine Ln");
Person originalPerson2 = new Person("Charlie", originalAddress2);
Person deepCopyPerson = originalPerson2.DeepCopy();
deepCopyPerson.Name = "David";
deepCopyPerson.HomeAddress.Street = "101 Elm Rd"; // Only modifies deepCopyPerson's address
Console.WriteLine($"\nOriginal Person (deep): {originalPerson2}");
Console.WriteLine($"Deep Copy Person: {deepCopyPerson}");
}
}