This is more of a java/groovy thing but I always assumed keys to a map were unique (in terms of .equals()). I found instances where I had duplicates and put together a test in the scriptrunner groovy console.
HashMap<Hashtable, String> map = new HashMap<>()
Hashtable<String, String> a1 = new Hashtable<>()
Hashtable<String, String> a2 = new Hashtable<>()
Hashtable<String, String> b = new Hashtable<>()
a1.put("a", "alpha")
a2.put("a", "alpha")
b.put("b", "bravo")
map.put(a1, "a1")
map.put(a2, "a2")
map.put(b, "b")
log.info("a1 equals a2: ${a1.equals(a2)}")
log.info("map: ${map}")
log.info("map containsKey a1: ${map.containsKey(a1)}")
log.info("map containsKey a2: ${map.containsKey(a2)}")
log.info("map containsKey b: ${map.containsKey(b)}")
Hashtable keyForA2 = map.find { it.value == "a2" }?.key
log.info("keyForA2: ${keyForA2}")
log.info("keyForA2 is a1: ${keyForA2.is(a1)}") // groovy ==
log.info("keyForA2 is a2: ${keyForA2.is(a2)}") // groovy ==
log.info("")
log.info("clearing a2, b")
a2.clear()
b.clear()
log.info("map: ${map}")
log.info("")
log.info("clearing a1")
a1.clear()
log.info("map: ${map}")
log.info("map.keySet(): ${map.keySet()}")
Hashtable keyForB = map.find { it.value == "b" }?.key
keyForA2 = map.find { it.value == "a2" }?.key
log.info("keyForB: ${keyForB}")
log.info("keyForA2: ${keyForA2}")
log.info("keyForB equals keyForA2: ${keyForB.equals(keyForA2)}")
log.info("keyForB is b: ${keyForB.is(b)}") // groovy ==
log.info("keyForA2 is a1: ${keyForA2.is(a1)}") // groovy ==
log.info("keyForA2 is a2: ${keyForA2.is(a2)}") // groovy ==
log.info("")
log.info("getting values")
String valA1 = map.get(a1)
String valA2 = map.get(a2)
String valB = map.get(b)
log.info("valA1: ${valA1}")
log.info("valA2: ${valA2}")
log.info("valB: ${valB}")
log.info("")
log.info("remove a2 from map")
valA2 = map.remove(a2)
log.info("valA2: ${valA2}")
log.info("map: ${map}")
log.info("")
log.info("remove a1 from map")
valA1 = map.remove(a1)
log.info("valA1: ${valA1}")
log.info("map: ${map}")
log.info("")
log.info("remove b from map")
valB = map.remove(b)
log.info("valB: ${valB}")
log.info("map: ${map}")
log.info("")
log.info("removing all map entries where keys are empty")
map.removeAll { Map.Entry<Hashtable, String> entry ->
boolean isEmpty = entry.key.isEmpty()
log.info("- isEmpty: ${isEmpty}")
isEmpty
}
log.info("map: ${map}")
This produced the following output
2021-10-19 10:57:59,406 INFO [runner.ScriptBindingsManager]: a1 equals a2: true
2021-10-19 10:57:59,406 INFO [runner.ScriptBindingsManager]: map: [[b:bravo]:b, [a:alpha]:a2]
2021-10-19 10:57:59,406 INFO [runner.ScriptBindingsManager]: map containsKey a1: true
2021-10-19 10:57:59,406 INFO [runner.ScriptBindingsManager]: map containsKey a2: true
2021-10-19 10:57:59,406 INFO [runner.ScriptBindingsManager]: map containsKey b: true
2021-10-19 10:57:59,407 INFO [runner.ScriptBindingsManager]: keyForA2: [a:alpha]
2021-10-19 10:57:59,407 INFO [runner.ScriptBindingsManager]: keyForA2 is a1: true
2021-10-19 10:57:59,407 INFO [runner.ScriptBindingsManager]: keyForA2 is a2: false
2021-10-19 10:57:59,407 INFO [runner.ScriptBindingsManager]:
2021-10-19 10:57:59,407 INFO [runner.ScriptBindingsManager]: clearing a2, b
2021-10-19 10:57:59,407 INFO [runner.ScriptBindingsManager]: map: [[:]:b, [a:alpha]:a2]
2021-10-19 10:57:59,407 INFO [runner.ScriptBindingsManager]:
2021-10-19 10:57:59,407 INFO [runner.ScriptBindingsManager]: clearing a1
2021-10-19 10:57:59,407 INFO [runner.ScriptBindingsManager]: map: [[:]:b, [:]:a2]
2021-10-19 10:57:59,407 INFO [runner.ScriptBindingsManager]: map.keySet(): [[:], [:]]
2021-10-19 10:57:59,408 INFO [runner.ScriptBindingsManager]: keyForB: [:]
2021-10-19 10:57:59,408 INFO [runner.ScriptBindingsManager]: keyForA2: [:]
2021-10-19 10:57:59,408 INFO [runner.ScriptBindingsManager]: keyForB equals keyForA2: true
2021-10-19 10:57:59,408 INFO [runner.ScriptBindingsManager]: keyForB is b: true
2021-10-19 10:57:59,408 INFO [runner.ScriptBindingsManager]: keyForA2 is a1: true
2021-10-19 10:57:59,408 INFO [runner.ScriptBindingsManager]: keyForA2 is a2: false
2021-10-19 10:57:59,408 INFO [runner.ScriptBindingsManager]:
2021-10-19 10:57:59,408 INFO [runner.ScriptBindingsManager]: getting values
2021-10-19 10:57:59,408 INFO [runner.ScriptBindingsManager]: valA1: null
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]: valA2: null
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]: valB: null
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]:
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]: remove a2 from map
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]: valA2: null
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]: map: [[:]:b, [:]:a2]
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]:
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]: remove a1 from map
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]: valA1: null
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]: map: [[:]:b, [:]:a2]
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]:
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]: remove b from map
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]: valB: null
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]: map: [[:]:b, [:]:a2]
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]:
2021-10-19 10:57:59,409 INFO [runner.ScriptBindingsManager]: removing all map entries where keys are empty
2021-10-19 10:57:59,410 INFO [runner.ScriptBindingsManager]: - isEmpty: true
2021-10-19 10:57:59,410 INFO [runner.ScriptBindingsManager]: - isEmpty: true
2021-10-19 10:57:59,410 INFO [runner.ScriptBindingsManager]: map: [[:]:b, [:]:a2]