Java集合1:Iterator和ListIterator

其实很久以前就想做这个系列了,因为深感数据结构的重要性,只是之前作业一直很多,没怎么有时间去弄这个,现在虽然也很忙,但还是抽出了时间来学习。

首先Java中集合框架有两大类,分别是Collection和Map,先来看Collection的父接口——Iterable


Iterable

Iterable接口很简单,主要是下面两个方法,

这些方法也没什么好研究的,这个类主要是为了生成一个Iterator对象,下面的Iterator才是重头戏


Iterator

Iterator就是C/C++中大家很常见的迭代器了,只是个接口,有下面几个方法

根据文档所说,它是用来代替原来Enumeration类,有两个优点。一是函数名比较短23333,二是调用者可以在迭代的途中删除元素,也就是说Enumeration在迭代过程中是不能删除元素的,会抛出错误。

  • void remove() 删除上一次next操作所返回的对象,如果没有执行过next会抛出错误
    值得注意的是这个方法并不是抽象方法,而是有默认实现的:
default void remove() {
    throw new UnsupportedOperationException("remove");
}

也就是说子类可以不实现这个方法,让这个迭代器不可在中途删除元素,例如如果是只读集合的话就可以不实现这个方法


ListIterator

ListIterator也是个接口,按照官方文档所说,它与Iterator不同之处有下面三点:
1.它是双向的,即既可以向前遍历也可以向后遍历
2.可以获得当前迭代器当前位置的索引
3.可以在迭代过程中修改列表的值

值得一提的是,迭代器里没有当前元素的概念,只有一个叫cursor的游标,它总是在元素之间,比如这样:

初始时它在第 0 个元素之前,调用 next() 游标后移一位:

调用 previous() 游标就会回到之前位置。当向后遍历完元素,游标就会在元素 N 的后面:

也就是说长度为 N 的集合会有 N+1 个游标的位置。

ListIterator是由Iterator继承而来的,新增加了6个方法,
  • boolean hasPrevious() 和hasNext()基本差不多,只不过返回前面是否有元素
  • E previous() 返回游标的前一个元素,同时向前进一步,如果没有前面元素则会抛出NoSuchElementException
  • int nextIndex() 返回调用next时会返回的元素的索引,也就是游标后面元素的索引
  • int previouxIndex() 与上面方法类似
  • void set(E) 设置上一次next()或previous()返回的元素为E,如果没有进行过next或previous操作则会抛出错误
  • void add(E) 在游标前面插入一个元素

ListIterator的具体实现

可以追踪到AbstractList里的listIterator方法,里面创建了个ListItr的实例,于是我们来看看ListItr

public ListIterator<E> listIterator(final int index) {
    rangeCheckForAdd(index);
    return new ListItr(index);
}

ListItr继承自Itr,Itr实现了Iterator接口

private class Itr implements Iterator<E> {

重点看一下next和remove方法

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();
      }
}

你会发现里面在开始都有一个checkForComodification的方法,这个方法是为了以防你在遍历的时候偷偷修改或删除元素的值的,让我们看看它的源码:

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

当expectedModCount和不相等时便会抛出错误,这里的modCount是AbstractList的成员变量,而expectedModCount是Itr的成员变量,它初始化为modCount

/**
* 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;

以ArrayList为例,调用add,clear和remove的时候,modCount都会修改,

public boolean add(E object) {
    //...
    modCount++;
    return true;
}

public void clear() {
    if (size != 0) {
        //...
        modCount++;
    }
}

public boolean remove(Object object) {
    Object[] a = array;
    int s = size;
    if (object != null) {
        for (int i = 0; i < s; i++) {
            if (object.equals(a[i])) {
                //...
                modCount++;
                return true;
            }
        }
    } else {
        for (int i = 0; i < s; i++) {
            if (a[i] == null) {
                //...
                modCount++;
                return true;
            }
        }
    }
    return false;
}

这样就实现了检测遍历途中是否发生了元素的添加以及修改

但如果确实要在遍历的时候添加元素、修改元素该怎么办呢?
还记得之前的ListIterator的特点吗?其中有一条就是可以在遍历中途修改元素

An iterator for lists that allows the programmer to traverse the list in either direction, modify the list during iteration, and obtain the iterator's current position in the list.

那么是如何实现的呢?

public void set(E e) {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        AbstractList.this.set(lastRet, e);
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

public void add(E e) {
    checkForComodification();

    try {
        int i = cursor;
        AbstractList.this.add(i, e);
        lastRet = -1;
        cursor = i + 1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

可以看到在修改和添加之后都会强制让expectedMod等于modCount
而且要注意的是不能调用AbstractList中的removeadd方法进行元素的添加和删除,而是要调用ListIterator中的removeadd方法
其他的代码都比较简单,就不在这里赘述了。

于是,Java集合框架中最简单的部分——Iterator就讲述到这里了,让我们下期再见!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容