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();