容器(四):List的大管家AbstractList
标签: Java编程思想
List家族的领导机制
我们在《容器(二):Collection三足鼎立之List》这篇笔记中,我们详细的介绍了List接口,而且还对其方法进行了介绍和使用。但是我们也发现了,在List接口中绝大部分的方法都没有实现。这表明,List作为领导,规定了大体的方向,至于方法的具体实现,都交给别人来完成。这个人就是List的大管家AbstractList。
作为管家的AbstractList,其实是被双重领导的。就像省检察院,是隶属于最高检,但是又对省委负责;AbstractList继承于AbstractCollection 抽象类,又实现了 List 接口。
List接口的子接口,在思想上接受List接口的领导,具体的工作则是AbstractList安排:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
//RandomAccess, Cloneable, java.io.Serializable是什么鬼以后再说,因为我现在也不知道
因此:
- list接口定义了所有list需要那些功能;
- AbstractList定义了一些公共的实现。
AbstractList源码分析
AbstractList的构造方法
protected AbstractList() {
}
唯一的构造方法(由子类构造方法调用,通常是隐式的)。
交由子类实现的方法
abstract public E get(int index);
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
索引方法的实现
indexOf(Object)
: 获取指定对象 首次出现 的索引.
public int indexOf(Object o) {
//获取 ListIterator,此时游标位置为 0
ListIterator<E> it = listIterator();
if (o==null) {
//向后遍历
while (it.hasNext())
//如果游标的后面是null,返回游标的前面元素索引(最后一个)
if (it.next()==null)
return it.previousIndex();
} else {
while (it.hasNext())
if (o.equals(it.next()))
return it.previousIndex();
}
return -1;
}
其核心的思路就是使用ListIterator迭代器向后遍历,找到与参数对象相同的元素。每次调用 listIterator.next() 方法 游标 都会后移一位,当 listIterator.next() == o 时(即找到我们需要的的元素),游标已经在 o 的后面,所以需要返回 游标的 previousIndex()。
lastIndexOf(Object)
: 获取指定对象最后一次出现的位置。
public int lastIndexOf(Object o) {
//获取 ListIterator,此时游标在最后一位
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() {
//传入由子类实现的 size()
removeRange(0, size());
}
protected void removeRange(int fromIndex, int toIndex) {
//获取 ListIterator 来进行迭代删除
ListIterator<E> it = listIterator(fromIndex);
for (int i=0, n=toIndex-fromIndex; i<n; i++) {
it.next();
it.remove();
}
}
迭代器it.next()有两个工作,一是指向下一个元素,而是将游标下移。
迭代方法
public Iterator<E> iterator() {
return new Itr(); //返回的是Itr()类的对象
}
public ListIterator<E> listIterator() {
return listIterator(0); //返回的是下面这个方法,参数为0
}
public ListIterator<E> listIterator(final int index) {
rangeCheckForAdd(index);
return new ListItr(index); //返回的是ListItr的对象
}
这三个迭代的方法都指向了两个类 Itr()
和ListItr()
,那么看来AbstractList的迭代器的具体实现就在这两个内部类上。
两个内部迭代器
AbstractList 内部已经提供了 Iterator, ListIterator 迭代器的实现类,分别为 Itr, ListItr, 不需要我们去帮他实现。
Itr()代码分析
Itr() 只是简单实现了 Iterator 的 next, remove 方法。
private class Itr implements Iterator<E> {
//游标,迭代器的核心
int cursor = 0;
//上一次迭代到的元素的位置,每次使用完就会置为 -1
int lastRet = -1;
//用来判断是否发生并发操作的标示,如果这两个值不一致,就会报错
int expectedModCount = modCount;
//判断有没有下一个元素的依据是:游标是否在列表的末尾
public boolean hasNext() {
return cursor != size();
}
//通过get()方法获取游标所对应的下标位置的元素,然后游标右移
public E next() {
//时刻检查是否有并发修改操作
checkForComodification();
try {
int i = cursor;
E next = get(i); // //调用 子类实现的 get() 方法获取元素
lastRet = i; //有迭代操作后就会记录上次迭代的位置
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
//
public void remove() {
//如果上一次迭代到的元素的位置为-1,则抛出异常
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
//处理方法是:调用接口中需要子类来实现的方法
//删除的是上一次迭代到的位置
AbstractList.this.remove(lastRet);
//如果游标位置大于上一次迭代到的位置,游标就前移
if (lastRet < cursor)
cursor--;
//删除后 上次迭代的记录就会置为 -1
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
//检查是否有并发修改
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ListItr() 代码分析
ListItr()
类继承了Itr()
类并实现了ListIterator接口,是 Itr()
的增强版。ListItr()
在继承了Itr()
类的方法的基础上多一些方法:
private class ListItr extends Itr implements ListIterator<E> {
//指定游标位置的构造参数
ListItr(int index) {
cursor = index;
}
//判断游标前是否有元素的依据是,游标是否为0
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) {
//如果上一次迭代到的元素的位置为-1,则抛出异常
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
//调用当前接口的,需要子类实现的set()方法
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);
//上一个迭代到的元素位置置为-1
lastRet = -1;
cursor = i + 1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
内部类:RandomAccess()和SubList()
由subList()引出
在Abstract接口中有这样一个方法:
public List<E> subList(int fromIndex,int toIndex)
:
返回列表中指定的 fromIndex到toIndex的左闭右开部分视图。(如果 fromIndex 和 toIndex 相等,则返回的列表为空)。返回的列表由此列表支持,因此返回列表中的非结构性更改将反映在此列表中,反之亦然。返回的列表支持此列表支持的所有可选列表操作。
也就是说:subList返回的只是原列表的一个视图,它所有的操作最终都会作用在原列表上
public List<E> subList(int fromIndex, int toIndex) {
return (this instanceof RandomAccess ?
new RandomAccessSubList<>(this, fromIndex, toIndex) :
new SubList<>(this, fromIndex, toIndex));
}
方法很简短,返回一个三目运算符:判断这个List是否属于RandomAccess的实例,如果是,就返回一个RandomAccessSubList对象,如果不是就返回一个SubList对象。
RandomAccess是啥
public interface RandomAccess {
}
RandomAccess 是一个空的接口,它用来标识某个类是否支持 随机访问(随机访问,相对比“按顺序访问”)。一个支持随机访问的类明显可以使用更加高效的算法。
List 中支持随机访问最佳的例子就是 ArrayList, 它的数据结构使得 get(), set(), add()等方法的时间复杂度都是 O(1);
不支持随机访问的就是 LinkedList, 链表结构使得它不支持随机访问,只能按序访问,因此在一些操作上性能略逊一筹。
通常在操作一个 List 对象时,通常会判断是否支持 随机访问,也就是:是否为 RandomAccess 的实例,从而使用不同的算法。
比如遍历,实现了 RandomAccess 的集合使用 get():
for (int i=0, n=list.size(); i < n; i++)
list.get(i);
比用迭代器更快:
for (Iterator i=list.iterator(); i.hasNext(); )
i.next();
SubList 源码
//SubList是AbstractList的子类
class SubList<E> extends AbstractList<E> {
private final AbstractList<E> l; //代表list
private final int offset;
private int size;
//构造函数有三个参数:List为操作的父类的list,fromIndex,toIndex为起始和终止位置
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; //将要操作的父类list赋给l
offset = fromIndex; //偏移位置为截取的起始位置
size = toIndex - fromIndex; //长度
this.modCount = l.modCount;
}
//以下方法是支持使用subList处理局部list的
//例如:list1.subList(100, 200).clear(); 很方便
//使用父类的 set()
public E set(int index, E element) {
rangeCheck(index);
checkForComodification();
return l.set(index+offset, element);
}
//使用父类的 get()
public E get(int index) {
rangeCheck(index);
checkForComodification();
return l.get(index+offset);
}
//截取后子list的大小
public int size() {
checkForComodification();
return size;
}
//index是子list的位置,在加上偏移量得到在原List的位置
//然后直接在原 List 上进行添加
public void add(int index, E element) {
rangeCheckForAdd(index);
checkForComodification();
l.add(index+offset, element);
this.modCount = l.modCount;
size++;
}
//index是子list的位置,在加上偏移量得到在原List的位置
//然后直接在原 List 上进行删除
public E remove(int index) {
rangeCheck(index);
checkForComodification();
E result = l.remove(index+offset);
this.modCount = l.modCount;
size--;
return result;
}
//调用父类的 局部删除
protected void removeRange(int fromIndex, int toIndex) {
checkForComodification();
l.removeRange(fromIndex+offset, toIndex+offset);
this.modCount = l.modCount;
size -= (toIndex-fromIndex);
}
//默认是在末尾位置添加
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
//从指定位置,直接在父类的原list上添加,更改size
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
int cSize = c.size();
if (cSize==0)
return false;
checkForComodification();
l.addAll(offset+index, c);
this.modCount = l.modCount;
size += cSize;
return true;
}
//也有迭代器 Iterator,实际上返回的是ListIterator
public Iterator<E> iterator() {
return listIterator();
}
//返回的是实现ListIterator接口的匿名内部类
public ListIterator<E> listIterator(final int index) {
checkForComodification();
rangeCheckForAdd(index);
//返回的是实现ListIterator接口的匿名内部类,指向的还是父类的原list的listIterator
return new ListIterator<E>() {
//i为父类原list的迭代器
private final ListIterator<E> i = l.listIterator(index+offset);
public boolean hasNext() {
return nextIndex() < size;
}
public E next() {
if (hasNext())
return i.next();
else
throw new NoSuchElementException();
}
public boolean hasPrevious() {
return previousIndex() >= 0;
}
public E previous() {
if (hasPrevious())
return i.previous();
else
throw new NoSuchElementException();
}
public int nextIndex() {
return i.nextIndex() - offset;
}
public int previousIndex() {
return i.previousIndex() - offset;
}
public void remove() {
i.remove();
SubList.this.modCount = l.modCount;
size--;
}
public void set(E e) {
i.set(e);
}
public void add(E e) {
i.add(e);
SubList.this.modCount = l.modCount;
size++;
}
};
}
public List<E> subList(int fromIndex, int toIndex) {
return new SubList<>(this, fromIndex, toIndex);
}
private void rangeCheck(int index) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
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();
}
}
总结:SubList 就是啃老族,虽然自立门户,等到要干活时,使用的都是父类的方法,父类的数据。所以可以通过它来间接操作父 List。
其中匿名内部类自己实现ListIterator的方法,并没有使用ListItr()方法
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);
}
}
RandomAccessSubList 只不过是在 SubList 之外加了个 RandomAccess 的标识,表明可以支持随机访问。
总结
AbstractList 作为 List 家族的大管家,继承于AbstractCollection,服务于 List ;实现了一些方法,还创建了供 List 家族内部使用的迭代器 Itr, ListItr 以及两个内部类 SubList 和 RandomAccessSublist ,可谓是尽心尽责。为 List 的子类们做出的榜样。
ps:用心学习,喜欢的话请点赞 (在左侧哦)