继承AbstractList实现只读的List(CountingIntegerList类)
本章例子来自java编程思想小节——17.2.3 使用Abstract类——其中的CountingIntegerList类。
import java.util.AbstractList;
public class CountingIntegerList extends AbstractList<Integer> {
private int size;
public CountingIntegerList(int size) {
this.size = size < 0 ? 0 : size;
}
public Integer get(int index) {//重写并实现
return index;
}
public int size() {//重写并实现
return this.size;
}
public static void main(String[] args) {
System.out.println(new CountingIntegerList(30));
}
}/* output:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
*///:~
查看输出发现,它居然能打印0-29出来,明明我们只是实现了get() & size()
方法。在之前的继承AbstractSet的例子里面(例子代码里面的静态内部类EntrySet
),继承AbstractSet
时需要实现的有iterator() & size()
方法,但这里继承AbstractList
时只需要实现get() & size()
方法就可以了。iterator()
相比get()
方法差别还是很大的,iterator()
返回的迭代器可以移动,并且每次移动则通过next()
方法取值,而get()
方法则是直接按照索引取值。
在debug模式下进行追踪,发现System.out.println(new CountingIntegerList(30))
会调用到AbstractCollection
的toString()
方法里面去,原来toString()
方法已经在AbstractCollection
里面就已经写好了(回忆一下AbstractList的类型声明:public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
)。
//AbstractCollection.java
public String toString() {
Iterator<E> it = iterator();//返回一个迭代器,用Iterator接口来接的
if (! it.hasNext())//如果迭代器刚开始就不能移动
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();//此时已经判断过hasNext可以返回true了
sb.append(e == this ? "(this Collection)" : e);//加入StringBuilder
if (! it.hasNext())//如果hasNext可以返回false,那么退出该方法
return sb.append(']').toString();
sb.append(',').append(' ');//如果hasNext可以返回true,那么先提前加好逗号空格
}
}
看上面代码还不知道这个迭代器的实现在哪里,但后面的逻辑则是正常打印一个集合的逻辑:反正已经有了迭代器,那么通过hasNext()
的返回值来决定要不要调用next()
来取值出来,每次取完值都把值的字符串表示加在一个StringBuilder上。
但iterator()
方法在AbstractCollection
中并没有实现,当然我们的CountingIntegerList
里面也没有去实现,所以它的实现只能在AbstractList
里:
//AbstractList.java
public Iterator<E> iterator() {
return new Itr();//调用成员内部类的构造器
}
private class Itr implements Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;//代表下一次调用next将会返回的元素索引
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.如果lastRet索引元素已经被删除,那么lastRet会置为-1
*/
int lastRet = -1;//代表上一次调用next或previous返回的元素索引
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;//获得外部类list对象的modCount成员,代表被修改的次数
public boolean hasNext() {
return cursor != size();//调用了例子代码重写的size方法。size刚好为最大索引+1,所以等于size时应该返回false
}
public E next() {
checkForComodification();
try {
int i = cursor;//既然cursor是下一次next返回的元素索引,那么next里就使用这个索引
E next = get(i);//调用了例子代码重写的get方法。这句是可能抛出IndexOutOfBoundsException的,当然本文例子不会抛出
lastRet = i;//next执行后,cursor就会变成last return
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)//如果自己已经remove过了,那么再次remove就会抛异常
throw new IllegalStateException();
checkForComodification();//如果别的线程也remove外部类list对象了,那么也会抛出异常
try {
AbstractList.this.remove(lastRet);//调用外部类对象的remove方法,但AbstractList里只是默认实现直接抛异常
if (lastRet < cursor)
cursor--;
lastRet = -1;//删除后就置lastRet为-1
expectedModCount = modCount;//再次获得外部类对象的modCount成员,因为remove后modCount会发生改变
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)//比较迭代器自己存的count,和外部类对象存的count
throw new ConcurrentModificationException();
}
}
发现原来有个成员内部类Itr
已经实现了Iterator接口,并在iterator()
方法返回return new Itr()
,这样每个迭代器对象都会持有一个外部类list对象的引用,而且list对象和迭代器对象的关系是一对多的。
成员内部类Itr
的类定义里调用了我们例子代码里重写的get() & size()
方法,具体看注释。继承AbstractList
时,其实还有些方法比如remove()/add()/set()
方法都是有默认实现的,但默认实现都是直接抛出UnsupportedOperationException异常,这种就是集合框架里面的可选操作,所以说本文例子代码只是一个只读的List,因为所有可选操作我们根本没有去重写实现它,只实现了必要的get() & size()
方法(这两个方法是抽象方法,所以必须实现)。