开发者

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 = [:[]]
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜