標籤:
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 call10 * to remove.11 */12 int lastRet = -1;13 14 /**15 * The modCount value that the iterator believes that the backing16 * List should have. If this expectation is violated, the iterator17 * 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); } }}
Java 集合:迭代器(Iterator, Iterable)