zoukankan      html  css  js  c++  java
  • Java容器解析系列(3) List AbstractList ListIterator RandomAccess fail-fast机制 详解

    做为数据结构学习的常规,肯定是先学习线性表,也就是Java中的List,开始
    Java中List相关的类关系图如下:

    此篇作为对Java中相关类的开篇.从上图中可以看出,List和AbstractList是表的具体实现类的抽象.

    首先我们来看一下List接口:

    /**
       list 表示一个序列,与Set不同,list通常允许重复元素和null;
       list还提供了一个特别的迭代器,ListIterator,其允许对元素进行插入和替换,并且允许双向的查询;
       因为可以向list中添加自身,这时就需要注意小心重写equals()方法和hashCode()方法;
     * @since 1.2
     */
    public interface List<E> extends Collection<E> {
    
        int size();
        boolean isEmpty();
        boolean contains(Object o);
        Iterator<E> iterator();
        Object[] toArray();
        <T> T[] toArray(T[] a);
        boolean add(E e);
        boolean remove(Object o);
        boolean containsAll(Collection<?> c);
        boolean addAll(Collection<? extends E> c);
        boolean addAll(int index, Collection<? extends E> c);
        boolean removeAll(Collection<?> c);
        boolean retainAll(Collection<?> c);
        void clear();
        boolean equals(Object o);
        int hashCode();
    
        E get(int index);
        E set(int index, E element);
        void add(int index, E element);
        E remove(int index);
        int indexOf(Object o);
        int lastIndexOf(Object o);
    
        ListIterator<E> listIterator();
        ListIterator<E> listIterator(int index);
    
        // 返回index在[fromIndex,toIndex)区间元素的一个视图(View),对返回的list进行的非结构性修改(non-structural changes)将会反映在当前list中,反过来,对当前list的非结构性修改也将会反映在返回的list中;
        // 返回的list将支持所有当前list支持的操作;
        // 结构性修改(Structural modifications):会导致list大小被改变的修改,或在迭代过程中,以其他方式扰乱迭代过程的修改;
        List<E> subList(int fromIndex, int toIndex);
    }
    

    List接口继承自Collection接口,其在Collection接口上增加了一些List的特有功能
    所有增加的方法都与索引有关

    1. 在指定索引位置添加元素;
    2. 删除指定索引位置元素;
    3. 修改指定索引位置元素;
    4. 依据索引查询元素;
    5. 找到指定元素的索引;
    6. 返回处于指定两个索引之间的元素视图;

    除此之外,还提供了一个新的迭代器,listIterator()返回一个ListIterator对象
    先来看ListIterator的源码:

    /**
       ListIterator允许:双向遍历list,迭代过程中修改返回的元素,获取迭代器的当前位置.
       一个ListIterator没有当前的元素这个概念,它的游标(cursor)位置永远处在会被previous()返回的元素和next()返回的元素之间;
      可能的游标位置:
                           Element(0)   Element(1)   Element(2)   ... Element(n-1)
      cursor positions:  ^            ^            ^            ^                  ^
       注意remove()方法和set()方法不是针对游标位置定义的,而是针对上一次通过previous()或next()返回的元素进行操作;
       上述对于游标位置的理解也适用于Iterator;
     * @since   1.2
     */
    public interface ListIterator<E> extends Iterator<E> {
    
        /* 这里继承了Iterator,但是还是把Iterator里面定义的方法重新写了一遍,至于为什么,I have no idea */
        boolean hasNext();
        E next();
        void remove();
    
        boolean hasPrevious();
        E previous();
    
        // 返回调用next()方会返回的元素的index,如果此时游标位于末尾,返回list的大小
        int nextIndex();
    
        // previous()会返回的元素的index,如果此时游标位于最前,返回-1
        int previousIndex();
    
        // 替换最近一次由previous()或next()返回的元素;
        // 该操作只有在previous()或next()后没有调用过remove()/add()才有效
        void set(E e);
    
        // 插入指定元素到next()将会返回的元素之前,previous()将会返回的元素之后;
        // 换言之,插入到游标位置前;
        void add(E e);
    }
    

    从上面的源码可以看出,ListIterator本身也是一个Iterator,不过其功能比Iterator更多,两种主要的区别如下:

    1. ListIterator可以双向遍历,Iterator只能单向遍历,即游标只能增大,不能变小;
    2. ListIterator可以添加/修改/删除元素,Iterator只能移除元素;
    3. ListIterator可以获取游标的位置,Iterator不能;

    具体关于ListIterator和Iterator的实现内容,还需看AbstractList的源码:

    /**
     * @since 1.2
     */
    public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    
        protected AbstractList() { }
    
        public boolean add(E e) {
            add(size(), e);
            return true;
        }
    
        public void add(int index, E element) {
            throw new UnsupportedOperationException();
        }
    
        abstract public E get(int index);
    
        public E set(int index, E element) {
            throw new UnsupportedOperationException();
        }
    
        public E remove(int index) {
            throw new UnsupportedOperationException();
        }
    
        public int indexOf(Object o) {
            ListIterator<E> it = listIterator();
            if (o == null) {
                while (it.hasNext())
                    if (it.next() == null)
                        return it.previousIndex();
            } else {
                while (it.hasNext())
                    if (o.equals(it.next()))
                        return it.previousIndex();
            }
            return -1;
        }
    
        public int lastIndexOf(Object o) {
            ListIterator<E> it = listIterator(size());
            if (o == null) {
                while (it.hasPrevious())
                    if (it.previous() == null)
                        return it.nextIndex();
            } else {
                while (it.hasPrevious())
                    if (o.equals(it.previous()))
                        return it.nextIndex();
            }
            return -1;
        }
    
        public void clear() {
            removeRange(0, size());
        }
    
        protected void removeRange(int fromIndex, int toIndex) {
            ListIterator<E> it = listIterator(fromIndex);
            for (int i = 0, n = toIndex - fromIndex; i < n; i++) {
                it.next();
                it.remove();
            }
        }
    
        public boolean addAll(int index, Collection<? extends E> c) {
            rangeCheckForAdd(index);
            boolean modified = false;
            for (E e : c) {
                add(index++, e);
                modified = true;
            }
            return modified;
        }
    
        public Iterator<E> iterator() {
            return new Itr();
        }
    
        public ListIterator<E> listIterator() {
            return listIterator(0);
        }
    
        public ListIterator<E> listIterator(final int index) {
            rangeCheckForAdd(index);
            return new ListItr(index);
        }
    
        /**
         * 该list被结构性修改(structurally modified)的次数
         * modCount被iterator()/listIterator()方法返回的Iterator/ListIterator对象使用.
         * 如果modCount的值被预料之外的(unexpectedly)修改,那么在迭代过程中的next()/
         * remove()/previous()/set()/add()方法调用会抛出ConcurrentModificationException,
         * 也就是,提供快速失效机制(fast-fail),以避免在并发情况下的不确定行为(non-deterministic behavior)
         *  如果子类 需
         * 实现fast-fail迭代器,那么需要在add()和remove(int)方法(还有其他会导致结构性修改的方法)中将该值自增1
         */
        protected transient int modCount = 0;
    
        private class Itr implements Iterator<E> {
    
            int cursor = 0;
            int expectedModCount = modCount;
            // 最近一次调用nex()或previous()返回的元素的索引
            // 每次调用remove后将该值置为-1
            int lastRet = -1;
    
            public boolean hasNext() {
                return cursor != size();
            }
    
            public E next() {
                checkForComodification();
                try {
                    int i = cursor;
                    E next = get(i);
                    lastRet = i;
                    cursor = i + 1;
                    return next;
                } catch (IndexOutOfBoundsException e) {
                    checkForComodification();
                    throw new NoSuchElementException();
                }
            }
    
            public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
                try {
                    AbstractList.this.remove(lastRet);
                    if (lastRet < cursor)
                        cursor--;
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException e) {
                    throw new ConcurrentModificationException();
                }
            }
    
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }
    
        private class ListItr extends Itr implements ListIterator<E> {
            ListItr(int index) {
                cursor = index;
            }
    
            public boolean hasPrevious() {
                return cursor != 0;
            }
    
            public E previous() {
                checkForComodification();
                try {
                    int i = cursor - 1;
                    E previous = get(i);
                    lastRet = cursor = i;
                    return previous;
                } catch (IndexOutOfBoundsException e) {
                    checkForComodification();
                    throw new NoSuchElementException();
                }
            }
    
            public int nextIndex() {
                return cursor;
            }
    
            public int previousIndex() {
                return cursor - 1;
            }
    
            public void set(E e) {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    AbstractList.this.set(lastRet, e);
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
    
            public void add(E e) {
                checkForComodification();
    
                try {
                    int i = cursor;
                    AbstractList.this.add(i, e);
                    lastRet = -1;
                    cursor = i + 1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
        }
    
        // 返回一个当前对象的视图(View),该视图中的元素范围为[fromIndex,toIndex)区间
        // 该视图的所有实际操作,实际都被委托到当前对象来处理
        public List<E> subList(int fromIndex, int toIndex) {
            return (this instanceof RandomAccess ? new RandomAccessSubList<>(this, fromIndex, toIndex)
                    : new SubList<>(this, fromIndex, toIndex));
        }
    
        public boolean equals(Object o) {
            if (o == this)
                return true;
            if (!(o instanceof List))
                return false;
    
            ListIterator<E> e1 = listIterator();
            ListIterator e2 = ((List) o).listIterator();
            while (e1.hasNext() && e2.hasNext()) {
                E o1 = e1.next();
                Object o2 = e2.next();
                if (!(o1 == null ? o2 == null : o1.equals(o2)))
                    return false;
            }
            return !(e1.hasNext() || e2.hasNext());
        }
    
        public int hashCode() {
            int hashCode = 1;
            for (E e : this)
                hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode());
            return hashCode;
        }
    
        private void rangeCheckForAdd(int index) {
            if (index < 0 || index > size())
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    
        private String outOfBoundsMsg(int index) {
            return "Index: " + index + ", Size: " + size();
        }
    }
    
    class SubList<E> extends AbstractList<E> {
        private final AbstractList<E> l;
        private final int offset;
        private int size;
    
        SubList(AbstractList<E> list, int fromIndex, int toIndex) {
            if (fromIndex < 0)
                throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
            if (toIndex > list.size())
                throw new IndexOutOfBoundsException("toIndex = " + toIndex);
            if (fromIndex > toIndex)
                throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
            l = list;
            offset = fromIndex;
            size = toIndex - fromIndex;
            this.modCount = l.modCount;
        }
    
        public int size() {
            checkForComodification();
            return size;
        }
    
        //  .....省略
        // 所有方法都是通过调用 成员变量l的同名方法 来实现,自身只处理界限检查之类的工作;
    }
    
    class RandomAccessSubList<E> extends SubList<E> implements RandomAccess {
        RandomAccessSubList(AbstractList<E> list, int fromIndex, int toIndex) {
            super(list, fromIndex, toIndex);
        }
    
        public List<E> subList(int fromIndex, int toIndex) {
            return new RandomAccessSubList<>(this, fromIndex, toIndex);
        }
    }
    

    限于篇幅,上述代码省略了AbstractList.SubList的部分方法,其中拥有的方法如下所示:

    AbstractList:

    1. 提供对List接口的基本骨架实现,这一点和AbstractCollection为Collection提供给了骨架实现一样;
    2. 为实现"随机存储"实现的List提供了一些公用方法,如果是顺序存储,应使用AbstractSequentialList;
    3. 如果是要实现一个不可变的List,只需要继承该类并实现get()和size();
    4. 如果是要实现一个可变的List,还需要重写set(int,E),如果是大小可变的,那么还需要重写add(int, E)和remove(int);

    而且,从上面可以看出:
    AbstractList实现的功能,都是通过listiterator()返回的ListIterator进行遍历查找来实现,而ListIterator实现功能需要的支持;

    除此之外,这其中有几个点需要特别注意的:

    1. Iterator和ListIterator的功能都是通过AbstractList.add()/remove()/set()/get()/size()来实现的;所以这几个方法,要么是可选操作,默认抛出UnsupportedOperationException,要么为抽象方法,子类必须实现;
    2. fail-fast机制

    根据AbstractList的源码注释,我们大概给出fail-fast机制的定义:在遍历过程中,不允许存在多个线程同时对同一个AbstractList进行结构性的修改,以避免在并发情况下的不确定行为(non-deterministic behavior),如果迭代器检查到了这种修改,那么抛出ConcurrentModificationException;

    fail-fast机制没有触发并不能保证没有发生并发修改;其应当仅仅被用于检测bugs.

    实现方式:

        1. 在AbstractList中有一个成员变量:modCount,表示当前对象的结构性修改次数;
        2. 如果需要实现fail-fast迭代器,那么需要在add()和remove(int)方法(还有其他会导致结构性修改的方法)中将该值自增1;
        3. AbstractList的迭代器也有一个成员变量:expectedModCount,在实例化的时候,将赋值为当前AbstractList.modCount;
        4. 在迭代过程中的next()/remove()/previous()/set()/add()方法调用时,先比较expectedModCount和modCount的值,如果不同(证明之前出现过结构性修改),抛出异常;
        5. 在迭代器的;add()/remove()方法调用的最后,将expectedModCount = modCount;
    

    关于fail-fast机制,推荐另一篇写得很好的博客:Java提高篇----fail-fast机制

    1. 迭代器操作还有一个逻辑:在调用next()/previous()方法返回一个元素后,只能调用一次remove()方法,且在调用remove()方法后,不能调用set()方法
      我们来看一下它的实现方式:
        1. 在Iterator方法中有一个成员变量:int lastRet = -1;其默认值为-1;
        2. 在调用next()/previous()方法时,会将lastRet置为返回的元素的游标位置值;
        3. 在调用remove()方法时,会将lastRet = -1;
        4. 每次调用迭代器的remove()/set()方法时,会先检查lastRet < 0,如果成立,抛出IllegalStateException;
    
    1. subList()实现,返回的是一个AbstractList的子类AbstractList.Sublist,返回的SubList对象中存在当前AbstractList对象的引用,且其中的所有操作都是通过调用当前AbstractList的同名方法来实现,而其自身只处理界限检查之类的工作;jdk中描述其实际为一个视图(View)可以说是很贴切了;
      如果当前AbstractList实现了RandomAccess接口,那么返回的对象为RandomAccessSubList类型,其继承了AbstractList.SubList,并且实现了RandomAccess接口;

    2. RandomAccess:

    /**
     * @since 1.4
     */
    public interface RandomAccess {
    }
    

    RandomAccess 是一个标记接口,表示一个List可以被随机访问,且使用get(int index)直接访问效率更高;
    不推荐使用迭代器来进行访问,迭代器只能按顺序访问list中的元素;
    也就是说,如果一个list实现了RandomAccess接口,那么代码:

    for (int i=0, n=list.size(); i < n; i++)
      list.get(i);
    

    比下面的代码运行快:

    for (Iterator i=list.iterator(); i.hasNext();)
        i.next();
    
    Let's go change the world,or changed by the world
  • 相关阅读:
    Java实现 LeetCode 400 第N个数字
    Java实现 LeetCode 400 第N个数字
    Java实现 LeetCode 399 除法求值
    Java实现 LeetCode 399 除法求值
    Java实现 LeetCode 399 除法求值
    Java实现 LeetCode 398 随机数索引
    Java实现 LeetCode 398 随机数索引
    Java实现 LeetCode 398 随机数索引
    linux中的cd ..和cd -命令有什么区别?
    GCC使用
  • 原文地址:https://www.cnblogs.com/jamesvoid/p/9779125.html
Copyright © 2011-2022 走看看