概述
业务需求,从一份excel表中取到X轴(项目)和Y轴(平台)的数据,和数据库中的数据进行比较,如果匹配不上,则把所有匹配不上的信息返回前端,当时采取的是
List<ProjectVo> shareProjects = projectMapper.selectAllShareProject();
List<ProjectVo> sharePlats = projectMapper.selectAllSharePlat();
for (int i = 0; i < header.getLastCellNum(); i++) {
Cell cell = header.getCell(i);
String projectName = cell.getStringCellValue();
for (ProjectVo shareProject : shareProjects) {
List<String> nameList = ExcelHelper.buildProjectUsedNames(shareProject);
String pureProjectName = ExcelHelper.trimSpaceAndSpecialSymbol(projectName);
if (nameList.contains(pureProjectName)) {
columnMap.put(columnId, shareProject.getId());
break;
}
}
}
这样只能判断excel中的现有数据是否和数据库匹配,无法判断数据库的数据是否全部存在excel表中,并且我觉得每次遍历一个完整的Collection让我觉得浪费性能。于是改成了如下
List<ProjectVo> shareProjects = projectMapper.selectAllShareProject();
Iterator<ProjectVo> shareProjectIterator = shareProjects.iterator();
List<ProjectVo> sharePlats = projectMapper.selectAllSharePlat();
Iterator<ProjectVo> sharePlatIterator = sharePlats.iterator();
String projectName = cell.getStringCellValue();
String pureProjectName = ExcelHelper.trimSpaceAndSpecialSymbol(projectName);
while (shareProjectIterator.hasNext()) {
ProjectVo item = shareProjectIterator.next();
List<String> nameList = ExcelHelper.buildProjectUsedNames(item);
if (nameList.contains(pureProjectName)) {
columnMap.put(columnId, item.getId());
shareProjectIterator.remove();
break;
}
}
String platName = cell.getStringCellValue();
String purePlatName = ExcelHelper.trimSpaceAndSpecialSymbol(platName);
while (sharePlatIterator.hasNext()) {
List<String> nameList = ExcelHelper.buildProjectUsedNames(sharePlatIterator.next());
if (nameList.contains(purePlatName)) {
rowMap.put(rowId, sharePlatIterator.next().getId());
sharePlatIterator.remove();
break;
}
}
这时候问题出现,当excel中的第一个cell遍历过后,后序所有的cell全部被判断为异常了,通断debug发现只有第一个cell能进入while(Iterator.hasNext())条件中,查看ArrayList源码
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
通过源码发现主要就是
int cursor; //当前索引
int lastRet = -1; //前一位索引
int expectedModCount = modCount; //Iterator 修改次数 = collection修改次数
hasNext() //返回 cursor != size()
next() //获取cursor指向的对象,并lastRet=cursor & cursor++
remove() //移除lastRet指向的对象,并cursor-- & lastRet=-1
checkForComodification() //判断集合的修改次数是否合法
当Collection调用iterator方法后,根据当前Collection对象,返回一个新的Iterator,
hasNext():
返回 cursor!=size()结果;
checkForComodification():
判断 modCount!=expectedModCount ,为true则抛出ConcurrentModificationException();
next():
调用Collection.get(cursor),返回cursor值指向的索引的元素,并lastRet=cursor,cursor++(这里就是第二次遍历无法进行的原因,
当Iterator遍历完成,cursor == Collection.size(),调用hasNext()时返回false),
当调用出现
IndexOutOfBoundsException()时,异常会被捕捉,并调用checkForComodification()方法,如果修改次数合法,则抛出
NoSuchElementException()
这里注意我的代码
List<String> nameList = ExcelHelper.buildProjectUsedNames(sharePlatIterator.next());
if (nameList.contains(purePlatName)) {
rowMap.put(rowId, sharePlatIterator.next().getId());
sharePlatIterator.remove();
break;
}
我这里调用了两次Iterator.next()方法,这会导致Iterator索引移动两次,数据不是预期的结果;
remove() :
首先判断lastRet<0,如果为true,则抛出异常,例 : 当你没有使用next()就直接remove()。
然后调用checkForComodification()方法,判断修改是否合法,接着调用ArrayList.remove(lastRet)(这里就是remote之前必须调用next()的原因,因为没有调用next(),lastRet很大概率=-1),接着判断lastRet<cursor(我觉得这有点多余,因为lastRet一直比cursor少),接着
cursor--; //下次使用next()时,就还是当前这个索引值,刚好和next()方法获取完cursor值的下标元素后lastRet=cursor,cursor++相对应
lastRet = -1;
expectedModCount = modCount;
回到最初的坑,结论:
当调用next()时,返回当前索引(cursor)指向的元素,然后当前索引值(cursor)会+1,如果你只是想在一次遍历中取到的元素都是同一个,Object ob = Iterator.next(),使用ob来进行你的业务逻辑,当所有元素遍历完,cursor == Collection.size(),此时再使用while(Iterator.hasNext())做循环条件时,返回的是false,无法进行下次遍历,如果需要多次使用Iterator进行遍历,当一次遍历完成,需要重新初始化Collection的iterator()。
最后
以上就是英俊钢铁侠为你收集整理的【记坑】Iterator遍历时,多次调用next(),二次遍历需要从Collection重新获取迭代器的全部内容,希望文章能够帮你解决【记坑】Iterator遍历时,多次调用next(),二次遍历需要从Collection重新获取迭代器所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复