ConcurrentModificationException在Android中访问MutableLiveData对象时的故障排除:

huangapple go评论103阅读模式
英文:

Troubleshooting: ConcurrentModificationException when accessing MutableLiveData Object in Android

问题

在这段代码中,您遇到了ConcurrentModificationException异常,这是因为您在迭代列表的同时尝试修改它。这种异常通常发生在使用迭代器迭代集合时,但在迭代期间尝试修改集合。

在您的情况下,您在while循环中迭代exam.getQuestions().get(questionPosition).getAnswers(),并在迭代过程中尝试修改exam.getQuestions().get(questionPosition).getAnswers()。这导致了ConcurrentModificationException异常的抛出。

为了解决这个问题,您可以尝试使用一个临时列表来保存要修改的答案,然后在循环结束后将这些修改应用到原始列表中,或者使用更安全的并发数据结构,如CopyOnWriteArrayList。

下面是一个示例,演示如何使用临时列表来避免ConcurrentModificationException异常:

List<Answer> answersToModify = new ArrayList<>();
Iterator<Answer> iterator = exam.getQuestions().get(questionPosition)
    .getAnswers().iterator();

while (iterator.hasNext()) {
    Answer answer = iterator.next();
    answer.setAnswer(answer.getId() == checkedId);
    answersToModify.add(answer);
}

// 将修改应用到原始列表中
for (int i = 0; i < answersToModify.size(); i++) {
    exam.getQuestions().get(questionPosition).getAnswers().set(i, answersToModify.get(i));
}

questionViewModel.getExam().postValue(exam);

try {
    questionViewModel.updateQuestion(1);
} catch (Exception e) {
    e.printStackTrace();
}
onUpdateQuestion();

这段代码创建了一个临时列表answersToModify,用于存储要修改的答案对象。在迭代过程中,将修改后的答案添加到临时列表中,然后在循环结束后将这些修改应用到原始列表中,避免了ConcurrentModificationException异常。这样,您就可以安全地修改答案对象而不会引发异常。

英文:

java.util.ConcurrentModificationException happened in MutableLiveData Object proxy design pattern

In android I have Class that named Exam , Exam Have a List of Question and each Question have a List of Answer , So I created ModelView Like :

private final MutableLiveData&lt;Exam&gt; liveExam;
...
    public MutableLiveData&lt;Exam&gt; getExam() {
        return liveExam;
    }
...
    public void updateQuestion(int nextOrPrevious) throws Exception {
        int indexOfQuestion = questionPosition.getValue() + nextOrPrevious;
        if (indexOfQuestion &gt;= 0 &amp;&amp; (exam.getQuestions().size() &gt;= (indexOfQuestion))) {
            mlQuestionText.postValue(liveExam.getValue().getQuestions().get(indexOfQuestion).getText());
            questionPosition.postValue(indexOfQuestion);
            mlExamQuestionNumber.postValue(TextUtil.toPersian(String.format(formattedQuestionNumberAndEndNumber, liveExam.getValue().getQuestions().get(indexOfQuestion).getIndex(), exam.getQuestions().size())));
        } else {
            throw new Exception(&quot;No more Question here&quot;);
        }
    }

everything work fine , but when I call :questionViewModel.getQuestionPosition().getValue().getQuestions()
.get(questionPosition).getAnswers()
if update answer like blow :

Iterator&lt;Answer&gt; iterator = exam.getQuestions().get(questionPosition)
.getAnswers().iterator();

while (iterator.hasNext()) {
         Answer answer = iterator.next();
answer.setAnswer(answer.getId() == checkedId);
exam.getQuestions().get(questionPosition).getAnswers().set(indexAnswer, answer);
      indexAnswer++;
                    }
                        questionViewModel.getExam().postValue(exam);
                      try {
                      questionViewModel.updateQuestion(1);
                         } catch (Exception e) {
                     e.printStackTrace();
                     }
       onUpdateQuestion();

I got an error for ConcurrentModificationException.

full print:

 java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.next(ArrayList.java:860)
at ir.mehritco.megnatis.cattell.ui.question.QuestionFragment$4.onCheckedChanged(QuestionFragment.java:185)                                                                   	at android.widget.RadioGroup.setCheckedId(RadioGroup.java:200)
at android.widget.RadioGroup.access$600(RadioGroup.java:62)
at android.widget.RadioGroup$CheckedStateTracker.onCheckedChanged(RadioGroup.java:385)
at android.widget.CompoundButton.setChecked(CompoundButton.java:221)
at android.widget.CompoundButton.toggle(CompoundButton.java:137)
at android.widget.RadioButton.toggle(RadioButton.java:77)
at android.widget.CompoundButton.performClick(CompoundButton.java:142)
at android.view.View.performClickInternal(View.java:7425)
at android.view.View.access$3600(View.java:810)
at android.view.View$PerformClick.run(View.java:28305)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

what happened here? and way I got this error?
I try some ways like store exam value in another Object and than post exam value or getQuestion from modelView as MutableLiveData but result as same!
My goal is :
Store answer(data) into answer(object)
so for this I need goto exam goto question list get question goto answer list get answer and updated

答案1

得分: 1

你不应该在迭代过程中修改集合,除非通过迭代器。你可以使用ListIterator来做到这一点:

ListIterator<Answer> iterator = exam.getQuestions()
        .get(questionPosition).getAnswers().listIterator();

while (iterator.hasNext()) {
    Answer answer = iterator.next();
    answer.setAnswer(answer.getId() == checkedId);
    iterator.set(answer);
}

或者你可以使用索引循环代替迭代器。

但在这种情况下,调用set() 没有意义,因为你没有更新answer的引用,只是更新了已经在列表中的对象的状态。因此,你可以简化为一个简单的for循环或使用forEach()

exam.getQuestions()
        .get(questionPosition)
        .getAnswers()
        .forEach(a -> a.setAnswer(a.getId() == checkedId));
英文:

You're not supposed to modify collections while iterating except via the iterator. You can use ListIterator to do that:

ListIterator&lt;Answer&gt; iterator = exam.getQuestions()
        .get(questionPosition).getAnswers().listIterator();

while (iterator.hasNext()) {
    Answer answer = iterator.next();
    answer.setAnswer(answer.getId() == checkedId);
    iterator.set(answer);
}

Or you could use an indexed loop instead of an iterator.

But in this case there's no point in calling set() at all, because you're not updating the answer reference, just the state of the object already in the list. So you can simplify this to a simple for loop or use forEach():

exam.getQuestions()
        .get(questionPosition)
        .getAnswers()
        .forEach(a -&gt; a.setAnswer(a.getId() == checkedId));

huangapple
  • 本文由 发表于 2023年5月26日 10:30:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/76337306.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定