java源码品读(6)— AbstractList

读完了List接口和AbstractCollection,我们来看AbstractList,AbstractList继承AbstractCollection并实现List接口,是ArrayList、Vector和Stack等重要类的超类。

它实现了 List 的一些位置相关操作(比如 get,set,add,remove),是第一个实现随机访问方法的集合类,但不支持添加和替换。还是老规矩,只看重写或新增的方法,其他的方法选择性略过。

  • 构造器
protected AbstractList() {
}

AbstractList是一个抽象类,也因此只提供了一个protected修饰的无参构造器,供子类使用。

  • add方法
public boolean add(E e) {
     add(size(), e);
     return true;
}

在指定的size()位置放入e元素,也就是在list的最后依次添加元素。

  • indexOf(Object o)方法
  • lastIndexOf(Object o)方法
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;
}

前者实现了list中的indexOf方法,通过迭代器的循环迭代检验指定元素是否出现在list中,如果出现即返回第一次出现时的下标,如果未出现则返回-1。后者实现了lastIndexOf方法,实现方式基本一样,只是迭代的顺序不同。

  • clear方法
public void clear() {
     removeRange(0, size());
 }

实现AbstractCollection的clear方法,实际调用新增的removeRange方法实现集合清空。

  • removeRange方法
protected void removeRange(int fromIndex, int toIndex) {
   ListIterator<E> it = listIterator(fromIndex);
   for (int i=0, n=toIndex-fromIndex; i<n; i++) {
       it.next();
       it.remove();
   }
}

AbstractList中新增的方法,目的是为了实现指定下标之间的元素移除,方法获取从fromIndex开始的list迭代器然后进行删除操作,可以省去遍历无用元素的时间,没有什么多余操作。

  • addAll方法
public boolean addAll(int index, Collection<? extends E> c) {
    //检查index的值是否在集合范围内
     rangeCheckForAdd(index);
     boolean modified = false;
     //将指定集合元素从index起依次添加进原集合中
     for (E e : c) {
         add(index++, e);
         modified = true;
     }
     //返回是否更改标识
     return modified;
 }
  • iterator方法
public Iterator<E> iterator() {
      return new Itr();
  }

实现了一个新的迭代器,下面是迭代器的详细代码,我们一点一点看来

private class Itr implements Iterator<E> {
        //游标指针,next方法即将会返回的元素下标
        int cursor = 0;
        //最近一次返回的元素的下标,执行删除操作会变回-1
        int lastRet = -1;
        //期望的修改次数,如果两个值不相同时,迭代器就发生了并发修改
        //protected transient int modCount = 0;
        //modCount是AbstractList中一个transient修饰的变量,表示集合结构大小变化的次数
        int expectedModCount = modCount;
        public boolean hasNext() {
            return cursor != size();
        }
        public E next() {
            //检查修改期望值,防止并发修改的现象出现
            checkForComodification();
            try {
                //每次执行next操作获取游标位置的元素,同时lastRet变为当前游标值后游标下移
                int i = cursor;
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                //二次检验确定是否是并发引起的问题
                checkForComodification();
                throw new NoSuchElementException();
            }
        }
        public void remove() {
            //当lastRet为-1两种情况,一种未执行过一次next操作,另一种执行过remove操作后未执行过next操作
            //两种情况下如果执行remove操作,前者游标尚未移动影响后续next操作,后者当前位置元素已经删除
            if (lastRet < 0)
                throw new IllegalStateException();
            //每次修改前检查,尽可能防止并发修改现象出现
            checkForComodification();

            try {
                //移除游标前一位置的元素,同时游标前移,lastRet变为-1,期望改变次数同步
                AbstractList.this.remove(lastRet);
                if (lastRet < cursor)
                    cursor--;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
  • listIterator
  • listIterator(final int index)
public ListIterator<E> listIterator() {
    return listIterator(0);
}
public ListIterator<E> listIterator(final int index) {
    rangeCheckForAdd(index);
    return new ListItr(index);
}

前者获取一个原始顺序的listIterator,后者获取一个游标从index开始的listIterator,下面是listItr的源码,它继承Itr,实现ListIterator。

private class ListItr extends Itr implements ListIterator<E> {
    //游标与Itr稍微区别是从传入的Index开始
    ListItr(int index) {
        cursor = index;
    }
    //增加了是否有前一个元素的方法
    public boolean hasPrevious() {
        return cursor != 0;
    }
    //获取前一个元素,实现跟Itr中的next基本类似不在多说
    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;
    }
    //新增的set方法,元素替换,同样不能出现在游标未移动或删除元素操作之后原因与上面的next方法一致
    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();
        }
    }
    //新增的add方法,实现方式与Itr中remove方法类似,添加的元素出现在原游标的位置,同时游标后移
    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();
        }
    }
}

从Itr和ListItr的源码中我们可以看到list中两种迭代器在多个操作中使用了双重检验的方式以避免出现多个线程同时操作迭代器的情况出现,所以在使用的时候都需要避免多线程的操作。

  • subList(int fromIndex, int toIndex)方法
 public List<E> subList(int fromIndex, int toIndex) {
        return (this instanceof RandomAccess ?
                new RandomAccessSubList<>(this, fromIndex, toIndex) :
                new SubList<>(this, fromIndex, toIndex));
}

根据list是否实现RandomAccess接口,返回随机访问的子list或者普通的子list
首先是RandomAccess,这是一个标记接口,用于标明实现该接口的集合支持快速随机访问,主要目的是使算法能够在随机和顺序访问的list中表现的更加高效。通俗点说ArrayList、Vector、Stack等实现了RandomAccess接口,我们可以通过下标快速找到对应的元素,LinkedList等没有实现RandomAccess接口,所以想要找到指定位置的元素就要麻烦一些。

这个是subList的部分源码,只列出了构造器的部分。

class SubList<E> extends AbstractList<E> {
    private final AbstractList<E> l;
    private final int offset;
    private int size;

    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;
    }

能看到的是sublist其实跟原来的list使用的是同样的数据,只是在原来List的基础之上增加了一个偏移量的概念,让生成的子集合能够正确的对应到原来的位置,其中一系列的方法跟AbstractList的实现方法基本一致,只是让偏移量这个东西参与到其中而且在每次进行子list的操作之前都会对modCount进行一次检查保证安全。也就是说如果你动了子List中的元素,原List中的元素也会跟着发生变化。

  • equals(Object o)方法
public boolean equals(Object o) {
    if (o == this)
        return true;
    //指定元素不属于List的时候返回false
    if (!(o instanceof List))
        return false;
    ListIterator<E> e1 = listIterator();
    ListIterator<?> e2 = ((List<?>) o).listIterator();
    while (e1.hasNext() && e2.hasNext()) {
        E o1 = e1.next();
        Object o2 = e2.next();
        if (!(o1==null ? o2==null : o1.equals(o2)))
            return false;
    }
    //长度不同时返回false
    return !(e1.hasNext() || e2.hasNext());
}

重写了或者说是细化了Object中的equals方法,Object中的equals(Object o)方法只对比了两个对象的地址,而AbstractList中有增加了是否是List和长度的判断,以及强转后两个List中集合元素的逐个判断(判断的时候需要依赖集合元素的equals方法)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值