Java 集合:迭代器(Iterator, Iterable)
Iterator接口
public interface Iterator<E> { boolean hasNext(); E next(); void remove(); }
访问元素前需要使用hasNext进行判断是否有元素存在,如果有再通过next操作获取,直接使用next操作而不进行hasNext检测,当到达末尾时会抛出NoSuchElement异常
Iterator的remove操作
好久没有看JDK代码了,今天翻看Java Core看到迭代器里面的注意点,居然一点都回忆不起来了。先看如下代码:
Iterator<String> iter = list.iterator(); String s = iter.next(); iter.remove();
那么这里iter.remove()删除的是哪个元素,删除的是列表中的第一个元素,通用一点来讲是迭代器上一次next()所返回的那个元素。又有如下代码:
Iterator<String> iter = list.iterator(); String s = iter.next(); iter.remove(); iter.remove();
如果去实际运行的话会报:java.lang.IllegalStateException异常即,每次remove都应该有对应的一次next,其实就是两两配对的,remove的就是next返回的那个元素。
从AbstractList的源码中可以看到Iterator的一个基本实现:
1 private class Itr implements Iterator<E> { 2 /** 3 * Index of element to be returned by subsequent call to next. 4 */ 5 int cursor = 0; 6 7 /** 8 * Index of element returned by most recent call to next or 9 * previous. Reset to -1 if this element is deleted by a call 10 * to remove. 11 */ 12 int lastRet = -1; 13 14 /** 15 * The modCount value that the iterator believes that the backing 16 * List should have. If this expectation is violated, the iterator 17 * has detected concurrent modification. 18 */ 19 int expectedModCount = modCount; 20 21 public boolean hasNext() { 22 return cursor != size(); 23 } 24 25 public E next() { 26 checkForComodification(); 27 try { 28 int i = cursor; 29 E next = get(i); 30 lastRet = i; 31 cursor = i + 1; 32 return next; 33 } catch (IndexOutOfBoundsException e) { 34 checkForComodification(); 35 throw new NoSuchElementException(); 36 } 37 } 38 39 public void remove() { 40 if (lastRet < 0) 41 throw new IllegalStateException(); 42 checkForComodification(); 43 44 try { 45 AbstractList.this.remove(lastRet); 46 if (lastRet < cursor) 47 cursor--; 48 lastRet = -1; 49 expectedModCount = modCount; 50 } catch (IndexOutOfBoundsException e) { 51 throw new ConcurrentModificationException(); 52 } 53 } 54 55 final void checkForComodification() { 56 if (modCount != expectedModCount) 57 throw new ConcurrentModificationException(); 58 } 59 }
可以看到有lastRet和cursor两个变量,前者用于代表next()操作返回的元素的索引,后者用于表示下一次next()调用是应该返回的元素的索引值。每当一次remove操作后lastRet就被清空了,同时cursor--,因为lastRet对应的元素在cursor前面,而此时其被remove了,那么cursor的值必然要减一。其实这里的迭代器实现都基本上被AbstractList的子类覆盖了,如LinkedList,ArrayList。前者不支持随机访问肯定不能用索引值作为获取元素的实现,否则迭代器效率就太低了。
ListIterator(extends Iterator<E>)
List接口除了继承Iterable接口外,还有几个额外的方法(listIterator)用来获取专门针对List的迭代器(即ListIterator)可以看一下LinkedList的迭代器实现:
private class ListItr implements ListIterator<E> { private Node<E> lastReturned = null; private Node<E> next; private int nextIndex; private int expectedModCount = modCount; ListItr(int index) { // assert isPositionIndex(index); next = (index == size) ? null : node(index); nextIndex = index; } public boolean hasNext() { return nextIndex < size; } public E next() { checkForComodification(); if (!hasNext()) throw new NoSuchElementException(); lastReturned = next; next = next.next; nextIndex++; return lastReturned.item; } public boolean hasPrevious() { return nextIndex > 0; } public E previous() { checkForComodification(); if (!hasPrevious()) throw new NoSuchElementException(); lastReturned = next = (next == null) ? last : next.prev; nextIndex--; return lastReturned.item; } public int nextIndex() { return nextIndex; } public int previousIndex() { return nextIndex - 1; } public void remove() { checkForComodification(); if (lastReturned == null) throw new IllegalStateException(); Node<E> lastNext = lastReturned.next; unlink(lastReturned); if (next == lastReturned) next = lastNext; else nextIndex--; lastReturned = null; expectedModCount++; } public void set(E e) { if (lastReturned == null) throw new IllegalStateException(); checkForComodification(); lastReturned.item = e; } public void add(E e) { checkForComodification(); lastReturned = null; if (next == null) linkLast(e); else linkBefore(e, next); nextIndex++; expectedModCount++; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
对于remove操作的思路大体一致只不过把lastRet换成了一个链表节点lastReturned,在每次remove后也会将其置位null。而在获取元素上不是像父类版本中的那样直接通过get(i)进行获取。迭代器会保存两个相邻的节点指针lastReturned和next。这样当元素被remove掉(lastReturned=null),当再次调用next时由于保存了next指针值,依然可以在链表中移动。
相比于Iterator接口ListIterator接口多了一个add方法,它会把元素放入到迭代器指向的next元素之前的位置,即下一个元素之前的位置。
Iterable接口
public interface Iterable<T> { /** * Returns an iterator over a set of elements of type T. * * @return an Iterator. */ Iterator<T> iterator(); }
如Java Core上所述如果我们实现Iterable接口那么就可以在foreach循环中使用。如
class MyCollection implements Iterable<Integer> { @Override public Iterator<Integer> iterator() { return new Iterator<Integer>() { public int count = 0; @Override public boolean hasNext() { return count < 10; } @Override public Integer next() { return count++; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } } public class Fields implements Const { public static void main(final String[] args) { MyCollection myCollection = new MyCollection(); for (Integer i : myCollection) { System.out.println(i); } } }