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的特有功能
所有增加的方法都与索引有关
- 在指定索引位置添加元素;
- 删除指定索引位置元素;
- 修改指定索引位置元素;
- 依据索引查询元素;
- 找到指定元素的索引;
- 返回处于指定两个索引之间的元素视图;
除此之外,还提供了一个新的迭代器,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更多,两种主要的区别如下:
- ListIterator可以双向遍历,Iterator只能单向遍历,即游标只能增大,不能变小;
- ListIterator可以添加/修改/删除元素,Iterator只能移除元素;
- 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:
- 提供对List接口的基本骨架实现,这一点和AbstractCollection为Collection提供给了骨架实现一样;
- 为实现"随机存储"实现的List提供了一些公用方法,如果是顺序存储,应使用AbstractSequentialList;
- 如果是要实现一个不可变的List,只需要继承该类并实现get()和size();
- 如果是要实现一个可变的List,还需要重写set(int,E),如果是大小可变的,那么还需要重写add(int, E)和remove(int);
而且,从上面可以看出:
AbstractList实现的功能,都是通过listiterator()返回的ListIterator进行遍历查找来实现,而ListIterator实现功能需要的支持;
除此之外,这其中有几个点需要特别注意的:
- Iterator和ListIterator的功能都是通过AbstractList.add()/remove()/set()/get()/size()来实现的;所以这几个方法,要么是可选操作,默认抛出UnsupportedOperationException,要么为抽象方法,子类必须实现;
- 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机制
- 迭代器操作还有一个逻辑:在调用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;
- subList()实现,返回的是一个AbstractList的子类AbstractList.Sublist,返回的SubList对象中存在当前AbstractList对象的引用,且其中的所有操作都是通过调用当前AbstractList的同名方法来实现,而其自身只处理界限检查之类的工作;jdk中描述其实际为一个视图(View)可以说是很贴切了; 
 如果当前AbstractList实现了RandomAccess接口,那么返回的对象为RandomAccessSubList类型,其继承了AbstractList.SubList,并且实现了RandomAccess接口;
- 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();
Java容器解析系列(3) List AbstractList ListIterator RandomAccess fail-fast机制 详解的更多相关文章
- Java容器解析系列(0)  开篇
		最近刚好学习完成数据结构与算法相关内容: Data-Structures-and-Algorithm-Analysis 想结合Java中的容器类加深一下理解,因为之前对Java的容器类理解不是很深刻, ... 
- Java容器解析系列(11) HashMap 详解
		本篇我们来介绍一个最常用的Map结构--HashMap 关于HashMap,关于其基本原理,网上对其进行讲解的博客非常多,且很多都写的比较好,所以.... 这里直接贴上地址: 关于hash算法: Ha ... 
- Java容器解析系列(4) ArrayList Vector Stack 详解
		ArrayList 这里关于ArrayList本来都读了一遍源码,并且写了一些了,突然在原来的笔记里面发现了收藏的有相关博客,大致看了一下,这些就是我要写的(╹▽╹),而且估计我还写不到博主的水平,这 ... 
- Java容器解析系列(1)  迭代的进化——从Enumeration到Iterator
		在Java中,对于所有的Collection,都有一个特性,可以通过迭代器来遍历和删除其中的元素,因为Collection接口继承自Iterable接口. public interface Colle ... 
- Java容器解析系列(5) AbstractSequentialList LinkedList 详解
		AbstractSequentialList为顺序访问的list提供了一个骨架实现,使实现顺序访问的list变得简单; 我们来看源码: /** AbstractSequentialList 继承自 A ... 
- Java容器解析系列(13) WeakHashMap详解
		关于WeakHashMap其实没有太多可说的,其与HashMap大致相同,区别就在于: 对每个key的引用方式为弱引用; 关于java4种引用方式,参考java Reference 网上很多说 弱引用 ... 
- Java容器解析系列(10) Map AbstractMap 详解
		前面介绍了List和Queue相关源码,这篇开始,我们先来学习一种java集合中的除Collection外的另一个分支------Map,这一分支的类图结构如下: 这里为什么不先介绍Set相关:因为很 ... 
- Java容器解析系列(7) ArrayDeque 详解
		ArrayDeque,从名字上就可以看出来,其是通过数组实现的双端队列,我们先来看其源码: /** 有自动扩容机制; 不是线程安全的; 不允许添加null; 作为栈使用时比java.util.Stac ... 
- Java容器解析系列(8) Comparable Comparator
		Comparable和Comparator接口是两个用于对对象进行大小比较的接口,在java集合相关类中,也被经常地使用到. 关于其使用,可以参考网络上的其他博客(没什么好说的);这里阐述关于这两个接 ... 
随机推荐
- [openjudge-搜索]单词接龙
			题目描述 描述 单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的"龙"(每个单词都最多在"龙&q ... 
- hdu5115 Dire Wolf
			题目链接 区间DP $dp_{i,j}$为杀掉$i~j$内的狼的最小代价 枚举$i~j$中最后杀掉的狼,$dp_{i,j}=min\{ { {k\in{[i,j]}} | dp_{i,k-1}+dp_ ... 
- SCI_Call_Bsw_SetPwmMotorGroupB
			Sci_Bsw.c -- definition MotorGroupB_cfg.c -- called in LatchControl_Magna.c: extern uint16 Sci_DRead ... 
- Vertical viewport was given unbounded height
			new Expanded( child: new ListView( ..... ) ); +++++++++++++++ 可以通过指定shrinkWrap = true为了你ListView. Li ... 
- 01:云计算三种服务模式SaaS、PaaS和IaaS
			1.1 云计算 1.什么是云计算 1. 云计算服务是指将大量用网络连接的计算资源统一管理和调度,构成一个计算资源池向用户按需服务. 2. 用户通过网络以按需.易扩展的方式获得所需资源和服务(资源包括网 ... 
- D3生成树专题
			这一天不知道怎的上课 竟然我说了两道题正解: 第一题:我写过一篇较详细的博客:https://www.cnblogs.com/Tyouchie/p/10366967.html 第二题:UVA10369 ... 
- iOS开发 -------- transform属性(形变)
			一 transform属性 在OC中,通过transform属性可以修改对象的平移,比例和旋转角度 常用的创建transform结构体的方法分两大类 (1) 创建"基于控件初始位置&qu ... 
- tomcat下面web应用发布路径配置 ( 即虚拟目录配置 )
			https://blog.csdn.net/AnQ17/article/details/52122236 
- css的再深入7(更新中···)
			1.transparent 透明的 2.placeholder 提示语 写页面 搞清结构层次, 保证模块化,让他们之间不能受到影响 (1) 元素性质 (2) 标准流 浮动带来的脱离文档流撑不起父级 ... 
- NYOJ 542 试制品(第五届河南省省赛)
			解法不唯一,但是还是set好理解而且用着爽,代码注释应该够详细了 #include<stdio.h> #include<string.h> #include<math.h ... 
