Java Iterator、ListIterator 解析

作为Java中常用的一个集合操作工具,他的重要性不言而喻。在Thinking In Java中作了如下的定义和描述:

The concept of an Iterator(another design pattern) can be used to achieve this abstraction. An iterator is an object whose job is to move through a sequence and select each object in that sequence without the client programmer knowing or caring about the underlying structure of that sequence.

简单来说就是简化了对集合的操作,让使用者不用去考虑集合本身的结构和实现。

Iterator是一个接口,其中定义了三个最基本的方法:

boolean hasNext();
E next();
void remove();

而在Java8中remove方法变成了default,还新增了一个default forEachRemaining方法,而default关键字也是Java8中新增的一个关键字在定义接口的时候方法加上default关键字则在其实现上可以不用实现,但默认方法必须提供一个实现。

首先要获取Iterator,而Collection集合实现了Iterable接口,所以直接调用iterator()方法即可。

next()方法获取集合中下一个元素,如果有的话。在此之前需要调用hasNext()方法来判断是否存在下一个元素。

所以对集合的操作常存在如下的操作:

while (iterator.hasNext()) {
    // to do something
}

而remove()方法则是集合操作中最重要的一个方法,这个方法会从集合中删除掉上一个返回的元素,而这个元素则是由next()方法返回的,也就是说在调用remove()方法的时候需要先调用next()方法。可以看到ArrayList remove()方法内部实现:

int cursor;       // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;

public boolean hasNext() {
    return cursor != size;
}

@SuppressWarnings("unchecked")
public E next() {
    checkForComodification();
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

源码可以看到remove之前会对集合上一个返回的元素下标进行判断,如果没有进行next()操作lastRet一直都是-1而ArrayList的remove方法remove掉的是下标为lastRet的元素,这就是为什么必须要先调用next()方法,然后将lastRet置再次置-1。特别要注意的是next方法的执行,切记多次调用,因为会将当前元素下标指向最新返回的元素下标,反复调用会导致数组越界。

再看ArrayList的remove方法实现:

public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

内部其实调用的是System的arraycopy方法,实际也是数组的处理,而next,remove等方法也是数字内部元素下标的移动,虽然iterator已经封装好了一系列方法,但是对iterator的操作也是要及其注意和小心的。

而ListIterator则是加强版的iterator,且只用于List。获取方法也类似,调用listIterator()方法即可。而内部方法也是继承了iterator的实现:

private class ListItr extends Itr implements ListIterator {
    ListItr(int index) {
        super();
        cursor = index;
    }

    public boolean hasPrevious() {
        return cursor != 0;
    }

    public int nextIndex() {
        return cursor;
    }

    public int previousIndex() {
        return cursor - 1;
    }

    @SuppressWarnings("unchecked")
    public E previous() {
        checkForComodification();
        int i = cursor - 1;
        if (i < 0) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i;
        return (E) elementData[lastRet = i];
    }

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

        try {
            ArrayList.this.set(lastRet, e);
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

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

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

多了一些对list的操作,获取上一个下一个元素的下标,是否有上一个元素,设置元素等等,内部实现也可以看出都是依赖数组的实现,操作也是一样需要对其方法需要严格控制调用以防数组越界报错。

以前对iterator的理解不够深入然后滥用造成了不小的麻烦,现在看了源码,对其更加了解。虽然iterator方便,但是他的使用是非常小心的,对集合的操作也是需要异常注意的。

  
BugHome版权所有丨转载请注明出处:https://minei.me/archives/279.html
  

发表评论

电子邮件地址不会被公开。 必填项已用*标注