1. 程式人生 > >迭代器Iterator和ListIterator

迭代器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不能新增元素,單向遍歷。

同:二者都是迭代器,當需要對容器進行遍歷且不需要干涉遍歷過程,都可以用。