继承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))会调用到AbstractCollectiontoString()方法里面去,原来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()方法(这两个方法是抽象方法,所以必须实现)。

posted @ 2019-11-10 16:23  allMayMight  阅读(239)  评论(0编辑  收藏  举报