Still learning the ropes with Groovy, this problem has tripped me up since last night. Not sure why it's throwing concurrentmod exception...(Java 1.6, Groovy 1.8.4)
I have a list of keys...
[1,2,3,4,5,6,7,8,9,10,11,12,13]
I collate the list using a custom function partitionList(keys,3)
I got from here (can't use java.List.Collate, not on 1.8.6)
Now I've got a list of lists...
[[0,1,2],[3,4,5],[6,7,8],[9,10,11],[12,13]]
If the number of sub-lists created is odd, I remove the last sub list [12,13]
and redistribute its keys amongst the other sub lists starting in order, creating...
[[0,1,2,12],[3,4,5,13],[6,7,8],[9,10,11]]
The exception occurs when iterating the values of the last sub list. Not sure why since I'm iterating a list and changing an entirely different list in that loop...
UPDATE:
Interesting....if I don't use the paritionList()
function, replacing
def keyRanges = partitionList( keys, 3)
with an explicit list of lists...
def keyRanges = [[0,1,2],[3,4,5],[6,7,8],[9,10,11],[12,13]]
the problem disappears. So I'm lead to believe the partitionList() function is doing something that's causing the exception
class CollateListTest {
static main(args) {
def keys = (0..13).toList()
//partition the keys into a list of lists
def keyRanges = partitionList( keys, 3)
println 'Key range sets...'
for( keyRange in keyRanges)
println keyRange
//if odd number of partitions,
//remove last sub list and redistribute values over other lists
if( (keyRanges.size() % 2) != 0){
def lastKeyRange = keyRanges.remove( keyRanges.size() - 1 )
println 'removed range: ' + lastKeyRange
// ** EXCEPTION HERE **
lastKeyRange.eachWithIndex{ k, index ->
println 'adding: ' + k
keyRanges[ index % keyRanges.size()].add( k)
}
}
}
//from stackoverflow.com/questions/2924395/
static def partitionList(list, size) {
def partitions = []
int partitionCount = list.size() / size
partitionCount.times { partitionNumber ->
def start = partitionNumber * size
def end = start + size - 1
partitions << list[start..end]
}
if (list.size() % size) partitions << list[partitionCount * size..-1]
return partitions
}
}
The partitionList
method you're using splits up the list with List.getAt(Range). This returns a view into the original list, but doesn't copy the data, so any modifications to the sublist also affect the original.
This means that lastKeyRange
shares data with keyRanges
, and that adding to one of the sublists indirectly affects the sublist you are iterating over. Rather than modifying the sublist, just create a new one. For example:
if( (keyRanges.size() % 2) != 0){
def lastKeyRange = keyRanges.remove( keyRanges.size() - 1 )
println 'removed range: ' + lastKeyRange
lastKeyRange.eachWithIndex{ k, index ->
println 'adding: ' + k
def newRange = []
newRange.addAll(keyRanges[ index % keyRanges.size()])
newRange.add(k)
keyRanges[ index % keyRanges.size()] = newRange
}
}