英文:
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<Exam> liveExam;
...
public MutableLiveData<Exam> getExam() {
return liveExam;
}
...
public void updateQuestion(int nextOrPrevious) throws Exception {
int indexOfQuestion = questionPosition.getValue() + nextOrPrevious;
if (indexOfQuestion >= 0 && (exam.getQuestions().size() >= (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("No more Question here");
}
}
everything work fine , but when I call :questionViewModel.getQuestionPosition().getValue().getQuestions()
.get(questionPosition).getAnswers()
if update answer like blow :
Iterator<Answer> 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<Answer> 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 -> a.setAnswer(a.getId() == checkedId));
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论