how to avoid ConcurrentModificationException kotlin

Kotlin

Kotlin Problem Overview


I have a list of accounts, and when i make the long click, I want to remove the item from the arraylist. I'm trying to remove it from a alertdialog, but i'm getting the ConcurrentModificationException. This is where is crashing:

listAccounts.forEachIndexed { index, account ->
    if (idParamether == account.id) {
        listAccounts.remove(account)
    }
}

Kotlin Solutions


Solution 1 - Kotlin

That's a common problem with the JVM, if you want to remove an item from a collection while iterating through it, you need to use the Iterators

exemple:

val myCollection = mutableListOf(1,2,3,4)
val iterator = myCollection.iterator()
while(iterator.hasNext()){
    val item = iterator.next()
    if(item == 3){
        iterator.remove()
    }
}

this will avoid ConcurrentModificationExceptions

I hope this answered your question, have a good day

Edit: you can find another explanation here, even if it is Java code the problem is the same
Edit n°2 the anwser of leonardkraemer show you a more kotlin-friendly way to do so

Solution 2 - Kotlin

In Kotlin you can use removeIf{ predicate }. Which is a shorthand to using the Iterator. Full statement:

listAccounts.removeIf{ it == account.id }

for the explanation see https://stackoverflow.com/questions/223918/iterating-through-a-collection-avoiding-concurrentmodificationexception-when-re

Update: Kotlin-stdlib introduced removeAll { predicate } which, as Aguragorn pointed out in his answer, does the same and can be used if removeIf is not present on your runtime environment (i.e. Java 1.6 or Android pre API level 24).

Solution 3 - Kotlin

with(listAccounts.iterator()) {
    forEach {
        if (it.id == someObj.id) {
            // do some stuff with it
            oldSubscription = it
            remove()
        }
    }
}

Same solution as SeekDaSky but more Kotlin'y

Solution 4 - Kotlin

It is actually removeAll { predicate } that kotlin stdlib introduced. So your code should look like this:

listAccounts.removeAll { it.id == idParamether }

see: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/remove-all.html

Note: when coding in kotlin I prefer sticking to kotlin APIs, it avoids problems like "Call requires API level 24"

Solution 5 - Kotlin

Try to use ConcurrentLinkedQueue instead of list to avoid this exception. As mentioned in ConcurrentLinkedQueue.Java, it orders elements FIFO (first-in-first-out).So it will avoid any problem with modifying a list while iterating it.
For exemple :

val list = ConcurrentLinkedQueue<String>()
list.add("toto")
list.add("tata")
list.add("titi")
list.forEachIndexed { index, it ->
    if (index % 2 == 0) {
        list.remove("tata")
        System.out.println(it)
    }
}

the out put is :
> toto
> titi

Solution 6 - Kotlin

I also had this problem and I solved it simply by cloning the starting list, so I go through that and add or remove elements in the original one.

This code gave me the exception:

 for(account in listAccounts){
    ....
    listAccounts.add(anotherAccount)
    ....

}

So just replace it with this:

val listAccountCloned = listAccounts.toMutableList()
 for(account in listAccountCloned){
    ....
    listAccounts.add(anotherAccount)
    ....

}

Solution 7 - Kotlin

I'd like to supplement Ryan's answer. If you'd like to add to your list during iteration, not just remove, you'd need to call .listIterator() on your collection instead of .iterator(). Then you'll have the add method too thanks to this interface. Complete code:

with(listAccounts.listIterator()) {
    forEach {
        if (it.id == someObj.id) {
            // do some stuff with it
            oldSubscription = it
            remove()
            add(obj)
        }
    }
}

Note: I know the OP just wanted to remove, but the title is more general and this is also the question you find if you search for adding in this situation too.

Solution 8 - Kotlin

You can make a copy of your list before iterating through it. It's a valid solution for small lists. That's what they are typically doing on Android before iterating through listeners for instance.

Solution 9 - Kotlin

the simplest way to solve this issue would be just adding "break" after the remove

for (i in accounts) {
                    if (nick == i.nick) {
                        print("Enter PIN: ")
                        var pin = scan.nextInt()
                        if (pin == i.pin) {
                            accounts.remove(i)
                            println("Account has been deleted successfully!")
                            break
                        }
                    }
                }

Solution 10 - Kotlin

solution as kotlin extension:

inline fun <T> List<T>.forEachIterable(block: (T) -> Unit) {
   with(iterator()) {
      while (hasNext()) {
         block(next())
      }
  }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionzasazView Question on Stackoverflow
Solution 1 - KotlinSeekDaSkyView Answer on Stackoverflow
Solution 2 - KotlinleonardkraemerView Answer on Stackoverflow
Solution 3 - KotlinRyanView Answer on Stackoverflow
Solution 4 - KotlinAguragornView Answer on Stackoverflow
Solution 5 - KotlinBrahim MasmoudiView Answer on Stackoverflow
Solution 6 - KotlinAdamo BranzView Answer on Stackoverflow
Solution 7 - KotlinTamás SajtiView Answer on Stackoverflow
Solution 8 - KotlinSlionView Answer on Stackoverflow
Solution 9 - KotlinAbdulaziz PulatjonovView Answer on Stackoverflow
Solution 10 - KotlinMarek KondrackiView Answer on Stackoverflow