迭代器Iterator和ListIterator
-
Iterator迭代器
屬於設計模式,提供了一個方法,對集合/容器內的元素進行遍歷,而不用關注底層實現細節,達到資料與上層遍歷解耦的目的.(解耦: 訪問邏輯從不同型別的集合類中抽取出來,接觸兩者間的聯合關係。)
- Iterator迭代器提供的三種方法:(迭代器直接操作集合中的資料)
boolean hasNext();//判斷集合中是否還有資料,有資料返回true;
E next();//獲取當前遍歷的元素
void remove();//刪除元素
- Iterable介面(返回一個迭代器)
public interface Iterable<T> {//介面實現 返回一個迭代器 /** * Returns an iterator over a set of elements of type T. * @return an Iterator. */ Iterator<T> iterator(); }
- 自定義實現Iterator中的三種方法
public class MyArrayList<T> implements Iterable<T>{ private T[] arr; private int size; public MyArrayList(){ this(10); } public MyArrayList(int n){ size = 0; arr = (T[])new Object[n]; } public boolean isEmpty () { return size == 0; } public boolean isFull () { return size == arr.length; } public void grow () { arr = Arrays.copyOf(arr, arr.length * 2); } public void add ( T data){ if (isFull()) { grow(); } arr[size++] = data; } public T get ( int index){ if (index < 0 || index > arr.length) { return null; } return arr[index]; } public int index(T data){ for(int i = 0;i < size ;i++){ if(arr[i] == data) return i; } return -1; } public void remove ( T data){ if(isEmpty()){ return; } int index = index(data); for(int i = index;i < size-1;i++){ arr[i] = arr[i+1]; } arr[size--] = null; } public void show () { for (T i : arr) { System.out.println(i); } } @Override public Iterator<T> iterator() { return new DIYItr(); } class DIYItr implements Iterator<T>{ int before; int after; public DIYItr(){ before = after = 0; } @Override public boolean hasNext() { return after != size; } @Override public T next() { int i = after; after = i+1; before = i; T result = arr[before]; return result; } @Override public void remove() { T r = arr[before]; MyArrayList.this.remove(r); } } public static void main(String[] args){ MyArrayList<Integer> list = new MyArrayList<>(); list.add(3); list.add(4); list.add(6); list.add(8); list.add(9); Iterator<Integer> itr = list.iterator(); while(itr.hasNext()){ Integer value = itr.next(); if(value == 6) { itr.remove(); } } for(Integer i : list){ System.out.print(i+" "); } } }
在程式執行中會發現:如果在迭代器進行遍歷時,進行改變集合大小結構的操作(增加,刪除等操作),會丟擲ConcurrentModificationException(併發異常);
在迭代之前,迭代器已經被創建出來了,如果此時改變容器的大小結構,IIterator物件無法同步list,Java會認為這樣的操作不安全,就會進行提醒,丟擲異常。從Iterator的實現原始碼可以看出:modCount是當前集合的版本號,每次add,remove,都會+1,expectedModCount是當前集合的版本號,迭代器初始化為modCount,而呼叫ArrayList.add(),或ArrayList.remove()時,只對modCount進行了操作,迭代器中的expectedModCount並未同步,導致在進行檢查時,丟擲異常。而使用Iterator.remove()就不會丟擲異常:原始碼發現,該方法會將modCount與expectedModCount同步。
- 丟擲併發性異常的原因是什麼呢?
- 快速失敗機制(fail-fast)和安全失敗機制(fail-safe)
- 快速失敗機制(fail-fast)
在迭代器進行遍歷時,不能刪除集合中的元素(改變集合大小結構的操作),否則會丟擲ConcurrentModificationException
原理:使用迭代器進行集合遍歷時,遍歷過程有一個modCount變數,對集合進行remove,add等操作時,對modCount都會進行改變,每當使用hasNext()或next()遍歷下一個元素時,都進行版本檢查checkForComidification(),當modCount==expectedMModCount時,繼續遍歷,否則,丟擲異常,終止遍歷。
- fair-safe機制:(安全失敗)
採用安全失敗機制的容器,在遍歷時,不是在原集合上直接操作,而是拷貝一份原集合,對拷貝的集合進行操作。
原理:由於操作的物件不是原集合,迭代器不能檢測到,所以不會丟擲併發異常。
缺點:由於遍歷操作的是初始拷貝的集合,所以無法對修改後的集合進行操作/訪問。
-
ListIterator提供雙向遍歷(有hasPrevious(),previous()),方法相對於Iterator迭代器提供了多個方法
-
Iterator和ListIterator的異同?(適用範圍和繼承結構)
異:適用範圍:Iterator可以適用於所有集合型別,而ListIterator只能用於List及其子型別。
涵蓋方法:ListIterator中有add(),可以新增元素,雙向遍歷,而Iterator不能新增元素,單向遍歷。
同:二者都是迭代器,當需要對容器進行遍歷且不需要干涉遍歷過程,都可以用。