Michail
Michail

Reputation: 37

What causes an Infinite loop when using put() vs putAll()

In the simple example code below, I wanted to quickly put the HashMap 'additional' into the payload of the Process object. However, I noticed that when I used .put() in order to nest the data deeper behind an additional key value("exampleData"), an infinite loop occurs:

public class Process {

    public Map<String, Object> getPayload() {
        return payload;
    }

    public void setPayload(Map<String, Object> payload) {
        this.payload = payload;
    }

    Map<String, Object> payload;
}
    public static void main(String[] args) {
        Process process = new Process();
        LinkedHashMap<String, Object> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("uuid", "uuid");
        process.setPayload(linkedHashMap);
        Map<String, Object> additional = new HashMap<>();
        additional.putAll(process.getPayload()); //additional.put("exampleData", process.getPayload());
        process.getPayload().put("additionalData",additional);
    }

When the code is executed with putAll(), no infinite loop occurs and the Process object looks like this:

enter image description here

However, when using put() instead of putAll(), an infinite loop occurs, with the payload containing itself and a StackOverFlowError occurs:

enter image description here

What is causing this infinite loop and why is putAll() immune to it?

Upvotes: 0

Views: 120

Answers (1)

Sweeper
Sweeper

Reputation: 270980

After the line:

additional.put("exampleData", process.getPayload());

additional now has a reference to the process payload. The relevant object graph looks like this:

process ---> payload <---- additional

(arrows represent references)

Then you did:

process.getPayload().put("additionalData",additional);

which adds to the payload, a new reference pointing to additional. Notice how this creates a cycle:

process ---> payload <---- additional
                |               ^
                |_______________|

This is fine, until you try to print the contents of payload. One of its values is additional so the contents of additional needs to be printed too, and one of additional's values is payload, so the contents of payload is printed again, and the loop continues... Unfortunately, the person who wrote the toString method for Map hasn't written anything to detect this.

putAll on the other hand,

Copies all of the mappings from the specified map to this map.

Therefore, additional.putAll(process.getPayload()); does not make additional have a reference to payload. Instead, it just adds whatever keys and values payload has, to additional.

In addition, it will also not create a cycle if you create a new map that has all the keys and values of payload, and add it to additional using put.

additional.put("exampleData", new HashMap<>(process.getPayload()));

Rather than having a reference to payload, additional would have a reference to a new map:

process ---> payload        additional ----> a copy of payload
                |               ^
                |_______________|

Upvotes: 1

Related Questions