Best way to initialize / addto Map values that are Lists in Groovy
I find myself repeatedly writing this code:
map[id] = map[id] ? map[id] + newListItem开发者_开发百科 : [newListItem]
Is there a more succinct way to initialize the value as a List or add to an existing list?
An alternative would be to use Map.withDefault
like so (taking Ted's example):
def map = [:].withDefault { [] }
def id = 'foo'
def newListItem = 'bar'
map[id] << newListItem
assert map[id] == ['bar']
map[id] << newListItem
assert map[id] == ['bar', 'bar']
Using the elvis (?:) operator is slightly more succinct:
def map = [:]
def id = 'foo'
def newListItem = 'bar'
map[id] = (map[id] ?: []) << newListItem
assert map[id] == ['bar']
map[id] = (map[id] ?: []) << newListItem
assert map[id] == ['bar', 'bar']
Though if speed is important, Kyle's answer is a bit quicker as it doesn't do the assignment step (tested 100,000 loops on my machine, his is 1.36s and mine is 1.46s vs 16.54s for the original).
I don't know of a more succinct way, but an alternative that's not much better looking is this:
if (map[id]) map[id] << newListItem else map[id] = [newListItem]
Just for kicks I ran each syntax in a loop for 100,000 iterations:
def m = [:]
for (def i=0; i<100000; ++i) {
//if (m['x']) m['x'] << 'val' else m['x'] = ['val']
m['x'] = m['x'] ? m['x'] + 'val' : ['val']
}
The time results for your syntax were:
real 0m45.367s
user 0m47.647s
sys 0m0.712s
The time results for this syntax were:
real 0m3.612s
user 0m5.920s
sys 0m0.252s
The results were consistent across several runs of each. So I would say this syntax is definitely preferred. I think the constant reassignment to the hash entry is what's slowing it down so much.
@Ted totally nailed it.
Just as an exercise, the curious/adventurous might try:
List.metaClass.onMap = { Map m = [:], id->
if(m[id])
m[id] << delegate
else
m[id] = [delegate]
}
def id = 'foo'
Map m = [:]
List l = [1,2,3]
m[id] = l.onMap(m,id)
From then on anytime you have a Map containing list values, you can use List.onMap() to populate.
You need Groovy 1.8 to use:
def map = [:].withDefault { [] }
This won't work:
def map = [:[]]
精彩评论