容器(四):List的大管家AbstractList

容器(四):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 &lt; 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:用心学习,喜欢的话请点赞 (在左侧哦)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值