所以我有这个方法应该在集合中找到对,为此目的,我使用嵌套循环。然而,我总是得到并发修改异常,即使我使用迭代器。我猜,因为两个迭代器都遍历同一个集合,它们都试图同时修改它,这就是为什么我得到这个异常。你能帮我避免这个错误,完成相同的结果吗?
private List<Pair<Document, Document>> createPairDocument(List<Document> documentsToIterate){
List<Pair<Document, Document>> pairDocList = new ArrayList<>();
//iterators are used to avoid concurrent modif exception
Iterator<Document> iterator0 = documents.iterator();
while(iterator0.hasNext()){
Document dl0 = iterator0.next();
Iterator<Document> iterator1 = documents.iterator(); //returns new instance of iterator
while(iterator1.hasNext()){
Document dl1 = iterator1.next();
if (dl1.relatedTo(dl0) && dl0.relatedTo(dl1)){
pairDocList.add(Pair.of(dl0, dl1));
//these docs should be removed to avoid creating the same relation again
iterator0.remove();
iterator1.remove();
break;
}
}
}
return pairDocList;
}
ConcurrentModificationException
发生的原因是,当迭代器遍历一个集合时,它不知道该集合被修改了,所以当该集合实际被修改时,迭代器变得非常困惑(具有无效状态)。通过使用Iterator.remove
方法,您可以让迭代器知道您正在删除元素,以便迭代器可以相应地调整其状态。
然而,在这个特殊的情况下,发生异常是因为iterator1
没有被告知iterator0
刚刚做的删除,在iterator0.remove();
行。当iterator1
试图删除它的元素时,它发现它的列表发生了变化。
使用两个迭代器遍历同一个列表不是一个好主意。我认为你可以使用常规的for循环来遍历列表的索引,每次从索引+ 1中获得一个列表迭代器,因为文档不能与自身相关。
for (int i = 0 ; i < documentsToIterate.size() ; i++) {
var iteratorFromI = documentsToIterate.listIterator(i + 1);
var dl0 = documentsToIterate.get(i);
while (iteratorFromI.hasNext()) {
var dl1 = iteratorFromI.next();
if (dl1.relatedTo(dl0) && dl0.relatedTo(dl1)){
pairDocList.add(Pair.of(dl0, dl1));
iteratorFromI.remove();
documentsToIterate.remove(i);
i--; // so that the next one doesn't get skipped
break;
}
}
}
现在我们没有并发修改异常,因为我们在iteratorFromI.remove()
之后做了documentsToIterate.remove(i);
,并且我们在之后丢弃了迭代器,所以它永远不会知道我们修改了列表:)
或者,只使用两个常规的for循环。
我还会改进算法,而不是一直检查所有元素的一个元素,而是尝试使用索引,并将第二个循环索引(j)基于第一个循环索引(I)的索引。不要做任何删除,并使用一个集合,以防你认为你可能有重复的列表,在这里已经建议。
for (int i = 0; i < documentsToIterate.size() - 1; i++) {
for (int j = i + 1; j < documentsToIterate.size(); j++) {
if (related(doc[i],doc[j]);
addPair(..);
}
}
也许你的问题可以很容易地从pairDocList
切换到pairDocSet
。
当你创建一个Set of PairDocuments时,你不需要从列表中删除任何元素。将同一个PairDocumentadd
两次或更多次添加到Set中是可以的,因为Set中没有重复项。您必须做出一些努力,用正确的equals()
和hashCode()
来识别相同的PairDocuments,但这是值得的。