1

In a filter function, I am trying to make a separate copy of a hashmap and remove some null elements from the original, however finally when I print the copyhashmap the same elements are removed from it too while I only want it to happen for the original one and the copy should stays unchanged.

This is my code;

def filter(log){

    Map logCopy = [:]
    logCopy.putAll((log))

    def indexedLog = logCopy.keySet()
    for(def i = 0; i <indexedLog.size(); i++){
        def subIndexedLog = logCopy[indexedLog[i]]
        def subIndexedLogEntrySet = subIndexedLog.keySet();
        for(def j = 0; j <subIndexedLog.size(); j++){ 
            if(subIndexedLog[subIndexedLogEntrySet[j]] == null){
                log[indexedLog[i]].remove(subIndexedLogEntrySet[j])
            }

        }
    }
    println logCopy
    println log
    return log;

}

def log = [letters:[a: null, b: null, c: "c"], numbers: [1: null, 2: null, 3: 3]]
filter(log)

Output;

[letters:[b:null, c:c], numbers:[2:null, 3:3]]
[letters:[b:null, c:c], numbers:[2:null, 3:3]]

I am so confused that when a null element is removed from log in the loop, why the same element is also removed from logCopy, and why b which indeed have a null value is not removed. Please help me with this problem, and also if you can suggest a better solution than my filter() function please do so. Thanks.

mentallurg
  • 4,967
  • 5
  • 28
  • 36
Saber
  • 107
  • 1
  • 10
  • you have map of maps. so you are making a clone of outer map but inner maps are the same for both clones. and `b` is now removed because of your algorithm (that i could not understand). – daggett Oct 05 '19 at 22:25

1 Answers1

2

Following example will help understand what is going on:

    a = [name: "Tom"]
    b = a
    println b.is(a)
    println a
    println b

    a["name"] = "Jerry"
    println a
    println b

    b["name"] = "Mouse"
    println a
    println b

Here a and b are two variables that point to the same object instance. That's why any change made to a mean also change in b and vice versa.

When you copy maps, then no copies of the objects will be created, but the same instances will be put to the logCopy. Following code for your objects has the same explanation as the example above:

    log["letters"]["a"] = "123"
    println log    ["letters"]["a"]
    println logCopy["letters"]["a"]

    logCopy["letters"]["a"] = "567"
    println log    ["letters"]["a"]
    println logCopy["letters"]["a"]

This explains why your changes on one map are also visible in another map.

What is different, are the map objects itself (not their contents). If you remove an element from the map itself, then you will see differences:

    log.remove("numbers")
    println logCopy
    println log

Such behaviour of putAll() is not a bug. It is how it is supposed to work. The same way it works in Java. If you want to create a deep copy of a map, you should do that manually: You should iterate over each member variable, create their copies, iterate over member variables of the nested objects, etc.

As a workaround you can serialize one map to a byte stream or to a to JSON and create a copy by deserializing. It is easier to implement than implementing deep copy method for each class. But check if performance if good for you.

mentallurg
  • 4,967
  • 5
  • 28
  • 36