Reputation: 37
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:
However, when using put() instead of putAll(), an infinite loop occurs, with the payload containing itself and a StackOverFlowError occurs:
What is causing this infinite loop and why is putAll() immune to it?
Upvotes: 0
Views: 120
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