What is the difference between a shallow copy and a deep copy, and how do you tell which one you have been given?
A shallow copy duplicates the outer object but copies its reference fields as references, so the copy and the original end up pointing at the same nested objects. A deep copy duplicates the whole reachable graph, so the two objects share nothing that can be mutated.
public class Main {
public static void main(String[] args) {
java.util.List<String> tags = new java.util.ArrayList<>();
tags.add("java");
Article original = new Article("OOP", tags);
Article shallow = new Article(original);
Article deep = new Article(original, true);
shallow.tags().add("shared");
System.out.println("original tags = " + original.tags());
System.out.println("deep tags = " + deep.tags());
}
}
class Article {
private final String title;
private final java.util.List<String> tags;
Article(String title, java.util.List<String> tags) {
this.title = title;
this.tags = tags;
}
Article(Article other) { this(other.title, other.tags); }
Article(Article other, boolean deep) {
this.title = other.title;
this.tags = deep ? new java.util.ArrayList<>(other.tags) : other.tags;
}
java.util.List<String> tags() { return tags; }
public String toString() { return title; }
}The output is the diagnostic. The shallow copy shares the list with the original, so adding a tag through the shallow copy changes what the original reports: both now show java and shared. The deep copy built its own list, so it still shows only java, untouched by the change.
How do you tell which one you have been given? Ask whether mutating a nested structure through the copy is visible through the original. If it is, you have a shallow copy and an aliasing bug waiting to happen. A useful design response is to make the exposed collections unmodifiable, so a caller physically cannot create the aliasing in the first place.
The deeper point is that a copy is only meaningful if every mutable field it holds is copied too. A class holding a list of mutable objects needs each element copied recursively, which is why deep copy code grows and why immutable fields are such a relief.