Realm-java: Crash when removing results in for loop.

Created on 24 Apr 2016  路  14Comments  路  Source: realm/realm-java

Hi, i am getting crash when removing RealmResults in for loop (Realm 0.89 with fixed iterators):

RealmResults<Foo> results = getResults();
for (Foo foo : results) {
    foo.deleteFromRealm();
}
T-Help

Most helpful comment

Reverse iterate your RealmList by index

for(int i = list.size()-1; i >= 0; i--) {
     Item item = list.get(i);
   ... 

All 14 comments

Hi @roughboy
Thank you for reporting.

I couldn't reproduce this.

I wrote the code below in RealmResultsTests.java. collection in the code is RealmResults<AllTypes>.

    @Test
    public void issue2660() {
        assertEquals(TEST_DATA_SIZE, collection.size());

        realm.beginTransaction();
        for (AllTypes foo : collection) {
            foo.deleteFromRealm();
        }
        realm.commitTransaction();

        assertEquals(TEST_DATA_SIZE, collection.size());
        realm.refresh();
        assertEquals(0, collection.size());
    }

Can you provide your stack trace?

Sorry, I totaly forgot this:

04-25 21:06:24.794 14883-15570/com.forzasoftware.forza D/REALM: rowIndex 3 > 2 - invalid!
04-25 21:06:24.795 14883-15570/com.forzasoftware.forza D/REALM: jni: ThrowingException 7, rowIndex > available rows: 3 > 2, .
04-25 21:06:24.795 14883-15570/com.forzasoftware.forza D/REALM: Exception has been throw: rowIndex > available rows: 3 > 2
04-25 21:06:24.811 14883-14883/com.forzasoftware.forza D/AndroidRuntime: Shutting down VM
04-25 21:06:24.814 14883-14883/com.forzasoftware.forza E/ACRA: ACRA caught a NoSuchElementException for com.forzasoftware.forza
                                                               java.util.NoSuchElementException: Cannot access index 3 when size is 2. Remember to check hasNext() before using next().
                                                                   at io.realm.RealmList$RealmItr.next(RealmList.java:824)
                                                                   at io.realm.RealmList$RealmItr.next(RealmList.java:781)
                                                                   at io.realm.RealmUtils.deleteSectionImpl(RealmUtils.java:431)
                                                                   at io.realm.RealmUtils.access$100(RealmUtils.java:27)
                                                                   at io.realm.RealmUtils$3.execute(RealmUtils.java:388)
                                                                   at io.realm.Realm$2.run(Realm.java:1316)
                                                                   at io.realm.internal.async.BgPriorityRunnable.run(BgPriorityRunnable.java:34)
                                                                   at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
                                                                   at java.util.concurrent.FutureTask.run(FutureTask.java:237)
                                                                   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
                                                                   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
                                                                   at java.lang.Thread.run(Thread.java:818)

I narrowed down the scenario. Please use following RealmObject:

public class RealmItem extends RealmObject {
    @PrimaryKey
    private long        id;
    private String      name;
    private RealmItem   item;
    private RealmList<RealmItem> childList;
}

The key is RealmList childList; The crash occurs when iterating over RealmList simultaneously removing items:

realm.beginTransaction();
for (RealmItem foo : item.getChildList()) {
    foo.deleteFromRealm();
}
realm.commitTransaction();

In case of problems, please use the example app (click FAB twice, and then click DELETE ALL to reproduce the issue):
https://drive.google.com/file/d/0B_iAqFNIegRpV19mRjM1VWMxa1E/view?usp=sharing

@roughboy thank you for providing detail information. I'll take a look.

I found this comment

    final void checkConcurrentModification() {
        // A Realm ListView is backed by the original Table and not a TableView, this means
        // that all changes are reflected immediately. It is therefore not possible to use
        // the same version pinning trick we use for RealmResults (avoiding calling sync_if_needed)

I think that comment explains the problem, I guess even though RealmResults<T> modifications are sent only on looper events, the RealmList still updates immediately when removing elements during a transaction? Is that possible?

I'm not a Realm person, haha.

@Zhuinden thanks!

@roughboy As @Zhuinden said, removing RealmList in for-each does not work.

Please use RealmList.deleteAllFromRealm() which has been added in 0.89.0 instead.

It is the same as removing items for normal ArrayLists. That is not allowed either and will throw ConcurrentModificationException.

In our case, actually detecting that concurrent modification is unfortunately rather hard, so it hasn't been implemented. Guess we should re-evaluate that decision.

I added an issue for tracking that /cc @zaki50 #2688

Hi @roughboy I assume the above answered you question. We will work on improving the situation in #2688

@cmelchior how would you delete the objects if not all were being deleted? i'm testing a condition for each object and testing true would mean it is deleted. i'm using a for each loop and am getting the same error.

final Integer itemClassId = 5;
final List<Item> items = UserSingleton.getUser().getItems();    //UserSingleton.getUser().getItems() is of type RealmList
if (items.size() != 0){
                RealmSingleton.getUserInstance().executeTransaction(new Realm.Transaction() {
                    @Override
                    public void execute(Realm realm) {
                        for (Item item : items){
                            if (item.getClassId().equals(itemClassId)){
                                item.deleteFromRealm();
                            }
                        }
                    }
                });
 }
java.util.NoSuchElementException: Cannot access index 1 when size is 0. Remember to check hasNext() before using next().
                              at io.realm.RealmList$RealmItr.next(RealmList.java:818)
                              at io.realm.RealmList$RealmItr.next(RealmList.java:775)

Reverse iterate your RealmList by index

for(int i = list.size()-1; i >= 0; i--) {
     Item item = list.get(i);
   ... 

@Zhuinden Thank you.
Can selective deletion of RealmList items be done in Kotlin, though? Kotlin doesn't have a "by index" for loop.

Since this comment was made we have a RealmResults.snapshot() method that keeps the collection stable even when objects are deleted.

Not to mention you could just do for(index in realmResults.size-1 downTo 0)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

AAChartModel picture AAChartModel  路  3Comments

bryanspano picture bryanspano  路  3Comments

cmelchior picture cmelchior  路  3Comments

wezley98 picture wezley98  路  3Comments

AlbertVilaCalvo picture AlbertVilaCalvo  路  3Comments