1. Overview

In this tutorial, we’ll explore the concept of a shallow vs deep copy of a HashMap along with several techniques to copy a HashMap in Java.

We’ll also consider some of the external libraries that can help us in specific cases.

2. Shallow vs Deep Copies

Firstly, let’s understand the concept of shallow and deep copies in HashMaps.

2.1. Shallow Copy

A shallow copy of a HashMap is a new HashMap with mappings to the same key and value objects as the original HashMap.

For example, we’ll create an Employee class and then a map with Employee instances as values:

public class Employee {
    private String name;

    // constructor, getters and setters
}
HashMap<String, Employee> map = new HashMap<>();
Employee emp1 = new Employee("John");
Employee emp2 = new Employee("Norman");
map.put("emp1", emp1);
map.put("emp2", emp2);

Now, we’ll verify that the original map and its shallow copy are different objects:

HashMap<String, Employee> shallowCopy = // shallow copy implementation
assertThat(shallowCopy).isNotSameAs(map);

Because this is a shallow copy, if we change an Employee instance’s properties, it will affect both the original map and its shallow copy:

emp1.setFirstName("Johny");
assertThat(shallowCopy.get("emp1")).isEqualTo(map.get("emp1"));

2.2. Deep Copy

A deep copy of a HashMap is a new HashMap that deeply copies all the mappings. Therefore, it creates new objects for all keys, values, and mappings.

Here, explicitly modifying the mappings (key-values) will not affect the deep copy:

HashMap<String, Employee> deepCopy = // deep copy implementation

emp1.setFirstName("Johny");

assertThat(deepCopy.get("emp1")).isNotEqualTo(map.get("emp1"));

3. HashMap API

3.1. Using the HashMap Constructor

HashMap‘s parameterized constructor HashMap(Map<? extends K,? extends V> m) provides a quick way to shallow copy an entire map:

HashMap<String, Employee> shallowCopy = new HashMap<String, Employee>(originalMap);

3.2. Using Map.clone()

Similar to the constructor, the HashMap#clone method also creates a quick shallow copy:

HashMap<String, Employee> shallowCopy = originalMap.clone();

3.3. Using Map.put()

A HashMap can easily be shallow-copied by iterating over each entry and calling the put() method on another map:

HashMap<String, Employee> shallowCopy = new HashMap<String, Employee>();
Set<Entry<String, Employee>> entries = originalMap.entrySet();
for (Map.Entry<String, Employee> mapEntry : entries) {
    shallowCopy.put(mapEntry.getKey(), mapEntry.getValue());
}

3.4. Using Map.putAll()

Instead of iterating through all of the entries, we can use the putAll() method, which shallow-copies all of the mappings in one step:

HashMap<String, Employee> shallowCopy = new HashMap<>();
shallowCopy.putAll(originalMap);    

We should note that put() and putAll() replace the values if there is a matching key.

It’s also interesting to note that, if we look at the HashMap‘s constructor, clone(), and putAll() implementation, we’ll find all of them use the same internal method to copy entries — putMapEntries().

4. Copying HashMap Using the Java 8 Stream API

We can use the Java 8 Stream API to create a shallow copy of a HashMap:

Set<Entry<String, Employee>> entries = originalMap.entrySet();
HashMap<String, Employee> shallowCopy = (HashMap<String, Employee>) entries.stream()
  .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

5. Google Guava

Using Guava Maps, we can easily create immutable maps, along with the sorted and bi map. To make an immutable, shallow copy of any of these maps, we can use the copyOf method:

Map<String, Employee> map = ImmutableMap.<String, Employee>builder()
  .put("emp1",emp1)
  .put("emp2",emp2)
  .build();
Map<String, Employee> shallowCopy = ImmutableMap.copyOf(map);
    
assertThat(shallowCopy).isSameAs(map);

6. Apache Commons Lang

Now, Java doesn’t have any built-in deep copy implementations. So to make a deep copy, either we can override the clone() method or use a serialization-deserialization technique.

Apache Commons has SerializationUtils with a clone() method to create a deep copy. For this, any class to be included in deep copy must implement the Serializable interface:

public class Employee implements Serializable {
    // implementation details
}

HashMap<String, Employee> deepCopy = SerializationUtils.clone(originalMap);

7. Conclusion

In this quick tutorial, we’ve seen various techniques to copy a HashMap in Java, along with the concept of shallow and deep copy for HashMaps.

Also, we explored some of the external libraries that are quite handy for creating shallow and deep copies.

The complete source code of these implementations along with the unit tests are available in the GitHub project.