'Processing list in Coroutines scope causes exception

I have a function that show markers (I call it tasks) on Google Maps. To make it cleaner, I chose an approach that iterates through the already displayed tasks and the new tasks. If a task already exists in maps and does not in the new list, I delete its marker. If the task is in both lists but its version has changed, I modify its marker . And if the task is only in the new list, i add its marker. Each time the user scrolls through the maps, I search for tasks based on the scrolled positon. The user can scroll multiple times, which should stop the previous call of the function.

the problem is that sometimes i get this exception

Fatal Exception: java.util.ConcurrentModificationException
       at java.util.ArrayList$Itr.next(ArrayList.java:860)
       at com.weryou.android.ui.missions.search.map.SearchMissionMapFragment$displayMissions$1.invokeSuspend(SearchMissionMapFragment.kt:435)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
       at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

And this exception

Fatal Exception: java.lang.IndexOutOfBoundsException: Index: 132, Size: 132
       at java.util.ArrayList.get(ArrayList.java:437)
       at kotlin.collections.CollectionsKt__MutableCollectionsKt.filterInPlace$CollectionsKt__MutableCollectionsKt(CollectionsKt__MutableCollectionsKt.java:284)
       at kotlin.collections.CollectionsKt__MutableCollectionsKt.removeAll(CollectionsKt__MutableCollectionsKt.java:269)
       at com.weryou.android.ui.missions.search.map.SearchMissionMapFragment$displayMissions$1.invokeSuspend(SearchMissionMapFragment.kt:220)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
       at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

This is my function

private val alreadyDisplayedMissions = ArrayList<Pair<MissionHeaderEntity, Marker>>()

private fun displayMissions(newMissions : List<MissionHeaderEntity>)
{
    lifecycleScope.launch(Dispatchers.Default) {
        
        val missionsToRemove : List<Pair<MissionHeaderEntity, Marker>> = alreadyDisplayedMissions.filter { oldMission ->
            
            val newVersionOfOldMission : MissionHeaderEntity? = newMissions.find { it.id == oldMission.first.id }
            
            newVersionOfOldMission == null || newVersionOfOldMission != oldMission.first
            
        }
        
        alreadyDisplayedMissions.removeAll { mission -> missionsToRemove.any { mission.first.id == it.first.id } }
        
        // Loop in the new missions to display in map all new missions with a determined location,
        // plus the tuto mission of the user if exists.
        val missionsToShow : List<Pair<MissionHeaderEntity, MarkerOptions>> = newMissions.filter { newMission ->
            (newMission.firstMission == true && newMission.owned == true)
            || alreadyDisplayedMissions.none { newMission.id == it.first.id }
            || (newMission.place?.address?.location?.latitude != null && newMission.place?.address?.location?.longitude != null)
        }.map { newMission ->
            
            // The icon resource of the marker to display
            val markerIcon : Drawable = when(newMission.state)
            {
                MissionState.BOOKED -> markerMissionBooked
                MissionState.AVAILABLE -> markerMissionAvailable
                MissionState.PRE_RELEASED -> markerMissionPreReleased
                else -> markerMissionOwned
            }
            
            // The icon of the marker to display .
            val markerBitmap : Bitmap = mapUtils.createMissionMarkerIcon(markerIcon, newMission.costing?.price.toCurrency(requireContext()))
            
            val markerLatitude : Double = newMission.place?.address?.location?.latitude!!
            val markerLongitude : Double = newMission.place?.address?.location?.longitude!!
        
            
            // The location of the marker to display.
            val markerLatLng = LatLng(markerLatitude, markerLongitude)
            
            val markerOptions : MarkerOptions = MarkerOptions()
                .position(markerLatLng)
                .icon(BitmapDescriptorFactory.fromBitmap(markerBitmap))
            
            Pair(newMission, markerOptions)
        }
        
        lifecycleScope.launch(Dispatchers.Main) {
            
            missionsToRemove.forEach { it.second.remove() }
            
            missionsToShow.forEach {
                
                val marker : Marker? = googleMap?.addMarker(it.second)
                marker?.tag = it.first
                
                alreadyDisplayedMissions.add(Pair(it.first, marker!!))
            }
        }
    }
}

What's wrong in my function and how to solve those exceptions?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source