75

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)
    }
}
2

12 Answers 12

75

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

You can find another explanation here, even if it is Java code the problem is the same

Sign up to request clarification or add additional context in comments.

3 Comments

I was adding the item while iterating! Thanks for the hint.
I'm afraid, This code does not help to avoid ConcurrentModificationException. For proper ting your need sama data, that are run by many threads. If some thera adds or delete items in collection, another thread still gets ConcurrentModificationException. Situuation does not cvhange are you using iretatir or indexes to collection. You get ConcurrentModificationException, when you have many threads handling same collection. Only solution to solve problem is use read and write locks to protect code handling collections in threads! If your don't use threads, then your con't get this exception.
Can confirm this code will still throw an the same exception, hasNext() is not safe if the size of the list has changed
45

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 Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop

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).

4 Comments

Not to forget that this Call requires API level 24
@VikasPatidar Yes API level 24 is required for Android, that introduces the Java 8 features necessary.
hey @leonardkraemer how to use this with adding item to list i have this error when adding can i use this : list.listIterator().add(item)
It's possible to use the method pre-API 24 with the core library desugaring feature introduced a while ago.
21
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

Comments

16

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"

Comments

9

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

1 Comment

Can you develop more your answer to make it more helpful?
6

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)
    ....

}

1 Comment

As Slion said before, this solution could be useful with small lists, but maybe could be a inefficient solution for lists with considerable size. Anyways, good solution! thanks!
2

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.

Comments

1

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.

Comments

1

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
                        }
                    }
                }

2 Comments

I don't know how but this actually worked. Strange
this will just exit the loop, no ?
1

solution as kotlin extension:

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

Comments

1

Or you can simply call .toList() over the original collection. That would create a brand new list to iterate through while you remove items from the original list.

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

Comments

0

Iterators are not always helpful when another thread also modifies the collection. I had tried many ways but then I realized traversing manually the collection is much safer (backward for remove):

for (i in listAccounts.size-1 downTo 0) {
    myList.getOrNull(i)?.apply {
       if (id == idParameter)
          listAccounts.remove(this)
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.