我是靠谱客的博主 英俊钢铁侠,最近开发中收集的这篇文章主要介绍【记坑】Iterator遍历时,多次调用next(),二次遍历需要从Collection重新获取迭代器,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

业务需求,从一份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重新获取迭代器所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部