Shallow vs Deep Copy in Java

Overview

1. Introduction

In this article, we'll examine the main differences between creating a deep and a shallow copy of an object.

2. Objects and References

In Java, every class is a sub-class of the Object class, which is a reference to a position in memory that holds some data. Objects are like pointers in those languages if you're familiar with some low-level languages like C or C++. They have a memory address and use it to access the stored data.

Since objects hold a reference, we can copy that reference and pass it to another object.

In Java, we can create objects that point to the original object's reference, without making a new one. That approach is known as a shallow copy.

We can also create new objects and copy the values field by field from the original object. That technique is called deep copy.

3. Shallow Copy

A shallow copy of an object refers to copying just its reference. That technique links the two objects in a way that they access the same memory position. Thus, any changes you make on the first object also apply to the second object. In shallow copy, there's no new memory allocation.

Shallow copies are helpful when accessing the same memory values in two different places in your code.

It is reasonable to mention that there are better practices than shallow copy when working with object copies. Modifying an object in two different places can lead to unexpected behaviors in your program. For example, we pass an object as an argument and modify it inside the method body. That might affect the code's behavior that accesses that object outside the method, leading to potential bugs.

4. Deep Copy

A deep copy of an object creates a new place in memory and copies all the fields from the original object to a new one. In that case, we're effectively creating a new object with the same members (including nested objects) as the original one.

Deep copy is a typical pattern to pass data along our programs in an input-transform-output style. Using deep copies in our application ensures that the objects have just one reference in memory, and thus their data are accessed in only one place. Therefore, when compared to shallow copies, deep copies tend to introduce fewer bugs to our application.

5. Examples of Shallow Copy and Deep Copy

Let's work with an example in Java to make things more transparent. First, consider the Article class that contains a title field and a nested object Tag:

 1public class Article {
 2
 3    private String title;
 4
 5    private Tag tag;
 6
 7    public Article(String title, Tag tag) {
 8        this.title = title;
 9        this.tag = tag;
10    }
11
12    public String getTitle() {
13        return title;
14    }
15
16    public Tag getTag() {
17        return tag;
18    }
19}

Let's also define the Tag class that is part of the Article class:

 1public class Tag {
 2
 3    private String name;
 4
 5    public Tag(String name) {
 6        this.name = name;
 7    }
 8
 9    public String getName() {
10        return name;
11    }
12
13    public void setName(String name) {
14        this.name = name;
15    }
16}

5.1. Example of Shallow Copy

The code below tests if any changes in the shallow copy affect the original object:

 1public class ShallowCopyTest {
 2
 3    @Test
 4    public void whenObjectIsShallowCopied_andCopyIsModified_thenOriginalIsAlsoModified() {
 5
 6        Article original = new Article("Java Shallow Copy vs Deep Copy Explained", new Tag("java"));
 7        Article copy = new Article(original.getTitle(), original.getTag());
 8
 9        copy.getTag().setName("python");
10
11        assertEquals("python", original.getTag().getName());
12    }
13}

We've created the copy object using the same Tag as the original one, using the original.getTag() method. Thus, the two object shares the same reference for their tag. Therefore, the changes made in the copy's Tag name transfers to the original object.

5.2. Example of Deep Copy

Contrary to a shallow copy, a deep copy should create a new object for the nested Tag of the copied Article. By doing that, the two objects don't share the same reference for their Tag field. Therefore, changing the tag value of copy should not affect the original object. Let's validate that with the unit test below:

 1public class DeepCopyUnitTest {
 2
 3    @Test
 4    public void whenObjectIsDeepCopied_andCopyIsModified_thenOriginalShouldNotModify() {
 5
 6        Article original = new Article("Java Shallow Copy vs Deep Copy Explained", new Tag("java"));
 7        Article deepCopy = new Article(original.getTitle(), new Tag(original.getTag().getName()));
 8
 9        deepCopy.getTag().setName("python");
10
11        assertNotEquals("python", original.getTag().getName());
12    }
13}

6. Conclusion

In this article, we've seen the differences between deep and shallow copy and their advantages.