• 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
  • 相关阅读:
    ios webview调试
    iOS与网页JS交互,看我就够了
    iOS开发工具-网络封包分析工具Charles
    使用 Charles 抓取 App 网络请求
    用css解决table文字溢出控制td显示字数
    Docker容器学习与分享03
    Docker容器学习与分享02
    Docker容器学习与分享01
    写在重新更新之前
    Kali学习笔记30:身份认证与命令执行漏洞
  • 原文地址:https://www.cnblogs.com/jamesvoid/p/9779125.html
Copyright © 2020-2023  润新知