B4J Question ConcurrentModificationException While Clearing Keys from Map

B4XDev

Member
Licensed User
I'm running this bit of code:

remove keys that match a pattern:
Public Sub ClearOBList
    For Each key As String In xassets.Keys 'xassets is just a map
        If key.Contains("-ob-") Then
            xassets.Remove(key)
        End If
    Next
End Sub

and getting this error: java.util.ConcurrentModificationException

I'm guessing it hasn't finished removing the last key before trying to remove the current key...?

Anybody have an idea? Maybe there's a better way to remove these keys?
 
Solution
1. You can use B4XOrderedMap and then iterate over the keys in reverse order. This is better than using Map.GetKeyAt which isn't built for this and will have an impact on performance if the map is large.
2. Another option that I usually do is:
B4X:
Dim KeysToRemove As List
KeysToRemove.Initialize
For Each key As String In Map.Keys
 If key.Contains("something") Then KeysToRemove.Add(key)
Next
For Each Key As String In KeysToRemove
 Map.Remove(Key)
Next

Daestrum

Expert
Licensed User
Longtime User
You might need to start at the end of the map keys and work backwards,. Not sure though.
 
Last edited:
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
B4X:
    For k = xassets.Size-1 To 0 Step -1
        If xassets.GetKeyAt(k).as(String).Contains("-ob-") Then
            xassets.Remove(xassets.GetKeyAt(k))
        End If
    Next
 
Upvote 0

B4XDev

Member
Licensed User
B4X:
    For k = xassets.Size-1 To 0 Step -1
        If xassets.GetKeyAt(k).as(String).Contains("-ob-") Then
            xassets.Remove(xassets.GetKeyAt(k))
        End If
    Next

This works fine, though I suspect Erel will condemn the use of GetKeyAt()... 😁

I will use this until an official reply comes through.
 
Upvote 0

B4XDev

Member
Licensed User
You might need to start at the end of the map keys and work backwards,. Not sure though.

I was thinking of using a Sleep(0) after the xassets.Remove(). What do you think about that? It might also conform to "best practices."

Update: That didn't seem to work for this use-case.
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
The only other way i know is to use inline java with streams to filter the map.
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
I tried sleep - didn't seem to work - only reading map backwards worked for me.
 
Upvote 0

LucaMs

Expert
Licensed User
Longtime User
I'm running this bit of code:

remove keys that match a pattern:
Public Sub ClearOBList
    For Each key As String In xassets.Keys 'xassets is just a map
        If key.Contains("-ob-") Then
            xassets.Remove(key)
        End If
    Next
End Sub

and getting this error: java.util.ConcurrentModificationException

I'm guessing it hasn't finished removing the last key before trying to remove the current key...?

Anybody have an idea? Maybe there's a better way to remove these keys?
Since keys are unique, there can only be one "-ob-", exit the loop when you find it.
B4X:
Public Sub ClearOBList
    For Each key As String In xassets.Keys 'xassets is just a map
        If key.Contains("-ob-") Then
            xassets.Remove(key)
            Exit ' <---
        End If
    Next
End Sub


For Each key As String In xassets.Keys 'xassets is just a map
That's a "nice" comment: "xassets is just a map"
If its name was "mapAssets" there would have been no need for that comment :)
 
Last edited:
Upvote 0

walt61

Active Member
Licensed User
Longtime User
@LucaMs re "Since keys are unique, there can only be one "-ob-", exit the loop when you find it." : yes but there can be an "-ob-1" and an "-ob-2" and an "-ob-wan-kenobi" :)

This does the trick:
B4X:
Public Sub ClearOBList
    Dim foundKey As Boolean = True
    Do While foundKey
        foundKey = False
        For Each key As String In xassets.Keys 'xassets is just a map
            If key.Contains("-ob-") Then
                xassets.Remove(key)
                foundKey = True
            End If
        Next
    Loop
End Sub
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
1. You can use B4XOrderedMap and then iterate over the keys in reverse order. This is better than using Map.GetKeyAt which isn't built for this and will have an impact on performance if the map is large.
2. Another option that I usually do is:
B4X:
Dim KeysToRemove As List
KeysToRemove.Initialize
For Each key As String In Map.Keys
 If key.Contains("something") Then KeysToRemove.Add(key)
Next
For Each Key As String In KeysToRemove
 Map.Remove(Key)
Next
 
Upvote 0
Solution

LucaMs

Expert
Licensed User
Longtime User
@LucaMs re "Since keys are unique, there can only be one "-ob-", exit the loop when you find it." : yes but there can be an "-ob-1" and an "-ob-2" and an "-ob-wan-kenobi" :)

This does the trick:
B4X:
Public Sub ClearOBList
    Dim foundKey As Boolean = True
    Do While foundKey
        foundKey = False
        For Each key As String In xassets.Keys 'xassets is just a map
            If key.Contains("-ob-") Then
                xassets.Remove(key)
                foundKey = True
            End If
        Next
    Loop
End Sub
I was sleeping standing up, unknowingly, and hadn't even noticed the "contains"! 😴😴😴😄:(
 
Upvote 0
Top