java.util.AbstractList学习笔记
概要
Java在不同维度上对列表进行了分类,如下图所示(如需要原xmind文件,请留言联系):
今天介绍的主要是随机访问列表的基础类AbstractList
。
构造方法
protected AbstractList() {
}
仍然是protected
的,与AbstractCollection
类似,相关资料可以查看:java.util.AbstractCollection学习笔记
内部类
迭代器基础类(Itr)
类声明
class Itr implements Iterator<E>
Itr
主要实现迭代器的基础功能,为链表迭代器ListItr
提供基础方法。
属性介绍
int cursor = 0;
:标记迭代器当前的游标位置,默认为0;
int lastRet = -1;
: 标记迭代器最后一次调用next()
或者previous()
访问的元素的坐标,当调用remove()
或者add()
操作时(注意:不一定是针对这个元素的操作),该值被设定为-1;
int expectedModCount = modCount;
:用于并发冲突检验,其中modCount
为当前列表被修改的次数,当迭代器进行迭代操作时,Itr
会根据expectedModCount == modCount
来判断在访问列表过程中,链表是否已经发生修改,如果发生修改,则抛出异常信息,其检测函数为:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
方法介绍
public boolean hasNext() {
return cursor != size();
}
通过判断游标是否与链表的长度相等来判断该游标后是否还有元素可供访问,这是Itr
中唯一一个不需要进行并发修改异常检验的方法;
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();
}
}
通过调用列表的get(int)
方法进行元素的访问,证明文章开头说的:对于随机访问列表,其迭代器通过调用列表的get方法实现。在获取元素后,需要同时移动游标和记录最后一次访问元素的坐标;
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();
}
}
删除迭代器最后一次访问的元素。最后一次的访问的元素坐标通过lastRet
记录。在完成删除后,由于数组的元素发生了位移,因此需要重新设定迭代器当前元素的坐标;
列表迭代器(ListItr)
类声明
class ListItr extends Itr implements ListIterator<E>
由此可见,ListItr
通过Itr
中实现的方法,实现列表迭代器的功能;
构造函数
ListItr(int index) {
cursor = index;
}
在构造一个新的列表迭代器时,需要制定迭代器当前处于列表中的位置。
方法介绍
public boolean hasPrevious() {
return cursor != 0;
}
根据游标是否为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();
}
}
返回迭代器游标之前的一个元素。并同时设定游标值和最后一次访问元素的坐标值。当调用previous
函数时,可能出现最后一次访问元素坐标与游标重合,其它情况下则不会出现。
public int nextIndex() {
return cursor;
}
获取下次调用next()
方法时返回的元素的下标;
public int previousIndex() {
return cursor-1;
}
获取下次调用previous()
方法时返回的元素的下标;
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();
}
}
将列表的最后一次访问的元素替换为e
;
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();
}
}
通过调用AbstractList
的add
方法将元素e
添加到迭代器当前的游标指示的位置中,在add方法中应该做了元素位置的重置。在完成添加后,将迭代器的游标指向下一个位置,由此可以看出,在同一个线程中,添加元素并不会影响调用next()
的返回值,即:同一个线程对列表的修改不会影响列表的遍历。
子列表(SubList)
类声明
class SubList<E> extends AbstractList<E>
该类的主要作用是对一个列表的子列表进行操作。
构造函数
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;
}
从构造函数可以看出,初始化一个子列表需要用户指定原始列表list
,开始坐标fromIndex
和结束坐标toIndex
。并将原始列表的修改次数保存到正在构造的子列表中。
主要属性
private final AbstractList<E> l;
:原始列表引用;
private final int offset;
:子列表相对于原始列表的偏移量,在构造函数中,初始化为```fromIndex``;
private int size;
:子列表的长度,在构造函数中初始化为toIndex - fromIndex
;
私有方法介绍
//判断请求访问的元素下标是否合法,不合法时,抛出IndexOutOfBoundsException,注意最后一个有效元素的下标为index - 1;
private void rangeCheck(int index) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 判断请求添加元素的下标是否合法,注意,当index==size时,则相当于调用add,在列表尾部追加一个元素;
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;
}
private void checkForComodification() {
if (this.modCount != l.modCount)
throw new ConcurrentModificationException();
}
公共方法介绍
public E set(int index, E element)
:将子列表中的第index
个元素设定为element
;
public E get(int index)
:获取子列表中的第index
个元素;
public int size()
:获取子列表的大小;
public void add(int index, E element)
:在index位置追加一个元素;
public E remove(int index)
:将子列表中下标为index的元素删除;
protected void removeRange(int fromIndex, int toIndex) {
checkForComodification();
l.removeRange(fromIndex+offset, toIndex+offset);
this.modCount = l.modCount;
size -= (toIndex-fromIndex);
}
将子列表处于fromIndex <= index < toIndex
区间的元素全部删除;
public boolean addAll(Collection<? extends E> c)
:将集合c中的元素全部追加到列表的尾部;
public boolean addAll(int index, Collection<? extends E> c)
:将集合c中的元素 全部插入到由index
开始的位置,如果c的大小大于0,返回true
,否则返回false
;
public Iterator<E> iterator() {
return listIterator();
}
调用AbstractList
中的listIterator()
方法,返回一个默认的迭代器;
public ListIterator<E> listIterator(final int index)
:针对SubList
的迭代器实现,其中index
为子列表相对于源列表的实现。其中的实现引用的是内部类ListItr
方法;
public List<E> subList(int fromIndex, int toIndex)
生成当前子列表的一个子列表;
与之相似的内部类还包括:随机访问子列表(RandomAccessSubList),在此不做过多解释,具体代码如下:
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);
}
}
方法介绍
public boolean add(E e)
:新增一个元素到列表尾部,该方法基于set(int index, E element)
实现;
public void add(int index, E element)
:新增一个元素到列表的指定位置,如果实现一个可变长度列表,则需要实现这个方法,默认抛出异常;
abstract public E get(int index);
:获取列表中指定位置的元素,该方法为抽象方法,需要子类进行实现;
public E set(int index, E element)
:设定列表中特定位置的元素为特定对象,默认抛出异常,如果实现一个可读写列表,则需要覆盖该方法;
public E remove(int index)
:删除列表中特定位置的元素,默认被抛出异常。如果需要实现一个可变长度的读写列表,则需要覆盖该方法;
public int indexOf(Object o)
:查找元素在列表中的位置。如果存在,则返回其对应下标,否则返回-1;在该方法的实现中,首先调用ListIterator
获得该列表的迭代器,而后便依次比较链表中的每个元素,如果相等,则返回迭代器访问的上一个游标,具体代码如下所示:
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)
:获取元素在列表中最后一次出现位置的下标,与indexOf
的实现方式类似,但是访问顺序为倒序;
public void clear()
:清空列表中的所有元素,内部使用removeRange(fromIndex, toIndex)
实现;
public boolean addAll(int index, Collection<? extends E> c)
将集合中的所有元素添加到列表的指定位置,其内部基于add(int, E)
实现;
public Iterator<E> iterator()
:获取当前列表的基本迭代器,其中仅包含:next()
, hasNext()
和remove()
方法;
public ListIterator<E> listIterator()
:获取当前列表的一个列表迭代器,功能较为全面,包括:hasNext()
, next()
, hasPrevious()
, previous()
, nextIndex()
, previousIndex()
, remove()
, set(E e)
, add(E e)
等;
ListIterator<E> listIterator(final int index)
:获取一个自定义游标的列表迭代器;
boolean equals(Object o)
:判断两个列表是否相等,当两个列表长度相等,且对应位置的元素也相等(equals
)时,则两个链表相等;
int hashCode()
生成列表的hash码;
protected void removeRange(int fromIndex, int toIndex)
:删除某一区间内的所有元素。调用listIterator(int)
获取列表迭代器,调用迭代器的next(), remove()
方法删除迭代器刚刚访问过的元素;