zoukankan      html  css  js  c++  java
  • 容器---List和AbstractList

    一、前言

      前面我们介绍了Collection及其抽象实现,在JAVA的容器体系里,由Collection派生出来的有两大体系,即List和Map。本文以及后续文章将重点分析List体系。本文将重点分析List接口定义及其抽象实现AbstractList.

    二、List接口定义

         还是先来看一下JDK对于List的定义:An ordered collection (also known as a sequence). The user of this interface has precise control over where in the list each element is inserted. The user can access elements by their integer index (position in the list), and search for elements in the list.

         通过上面的定义我们知道,list是一个有序的collection,可以通过index进行精确的插入,访问和查找。相比于collection来说,list接口的定义增加了通过index的一系列操作。List接口新增的方法如下 :

         1. 添加: 有add(int index, E e), addAll(int index, E e)

         2. 修改: set(int index, E e)

         3. 删除:remove(index)

         4. 查找: indexOf(int index), lastIndexOf(int index), get(int index)

         5. 遍历:listIterator(), listIterator(int index), subList(int fromIndex, int toIndex)

         可以看到新增的方法提供了更精确的根据index来对列表进行操作,充分体现了list的特点,另外提供了更强大的遍历方法,即listIterator和subList.

    三、AbstractList 介绍

          AbstractList 继承自AbstractCollection,同时实现了List接口,提供了很多方法的默认实现,但这仍然是一个抽象类,我们可能通过继承该类,来快速地实现一个List. 对AbstractList的特点如下:

          1.  所有的add方法都会转化为调用add(index, E e), 而方法默认是不被支持的

          2.  同样不被支持的还有remove(int index),和set(int index, E e)

          3.  get(int index)是一个惟一的抽象方法,没有实现。

          4.  iterator方法不再是抽象的,将返回一个内部类实现

          5.  size() 方法仍然是抽象的

          从上面的特点我们可以看到,AbstractList并没有定义如果通过下标来操作list,这是因为不同的具体list类的数据结构不一样,访问方式也就不一样,而即使不实现,也不影响对于整个list的遍历,而如何获取某个位置的元素取决于get(index),所以这个方法是抽象的。

    四、关键方法介绍

          新增的这几个方法还是比较好理解的,我们重点对iterator和subList的实现原理进行介绍。

          1. iterator() 方法很简单,直接返回了一个新的Itr实例: return new Itr();该类的源码如下:

     1 private class Itr implements Iterator<E> {
     2         /**
     3          * Index of element to be returned by subsequent call to next.
     4          */
     5         int cursor = 0;
     6 
     7         /**
     8          * Index of element returned by most recent call to next or
     9          * previous.  Reset to -1 if this element is deleted by a call
    10          * to remove.
    11          */
    12         int lastRet = -1;
    13 
    14         /**
    15          * The modCount value that the iterator believes that the backing
    16          * List should have.  If this expectation is violated, the iterator
    17          * has detected concurrent modification.
    18          */
    19         int expectedModCount = modCount;
    20 
    21         public boolean hasNext() {
    22             return cursor != size();
    23         }
    24 
    25         public E next() {
    26             checkForComodification();
    27             try {
    28                 int i = cursor;
    29                 E next = get(i);
    30                 lastRet = i;
    31                 cursor = i + 1;
    32                 return next;
    33             } catch (IndexOutOfBoundsException e) {
    34                 checkForComodification();
    35                 throw new NoSuchElementException();
    36             }
    37         }
    38 
    39         public void remove() {
    40             if (lastRet < 0)
    41                 throw new IllegalStateException();
    42             checkForComodification();
    43 
    44             try {
    45                 AbstractList.this.remove(lastRet);
    46                 if (lastRet < cursor)
    47                     cursor--;
    48                 lastRet = -1;
    49                 expectedModCount = modCount;
    50             } catch (IndexOutOfBoundsException e) {
    51                 throw new ConcurrentModificationException();
    52             }
    53         }
    54 
    55         final void checkForComodification() {
    56             if (modCount != expectedModCount)
    57                 throw new ConcurrentModificationException();
    58         }
    59     }
    View Code

      通过分析源码,我们可以看到这是一个内部类,完整的实现了iterator接口,定义了两个变量cursor及lastRet,cursor代表了迭代器中的下一个元素的位置,初始时为0,即初始时,下一个要遍历的元素的下标是0,lastRet表示刚刚遍历后的元素的下标,默认为-1。其hasNext方法是判断cursor是否为size(), 这个表示遍历结束,可以多次调用而不影响迭代元素的位置。

          对于next()来说,其主要逻辑是返回当前元素,并更新cursor为下一个元素的下标,更新lastRet为当前元素的下标,这个元素也是刚被遍历过的。所以,对于next()方法来说,也可以多次调用,只是在执行逻辑时,next()方法会先检查下标的范围是否越界,如果越界会抛出异常。

          而对于remove()方法来说,每执行一次,lastRet的值会变成-1, cursor的值会减1,而remove方法调用前会先判断lastRet的值是否小于0,如果小于0则会抛出异常,所以这个remove()方法只能被调用一次,必须再调用一次next()方法后才能再调用remove().

          对于remove()和next()来说,最终的实现还是依赖于AbstractList中的remove和get方法,所以其行为还是由list来决定的。

          2. listIterator()方法返回一个ListItr对象,这个实现如下:

     1 private class ListItr extends Itr implements ListIterator<E> {
     2         ListItr(int index) {
     3             cursor = index;
     4         }
     5 
     6         public boolean hasPrevious() {
     7             return cursor != 0;
     8         }
     9 
    10         public E previous() {
    11             checkForComodification();
    12             try {
    13                 int i = cursor - 1;
    14                 E previous = get(i);
    15                 lastRet = cursor = i;
    16                 return previous;
    17             } catch (IndexOutOfBoundsException e) {
    18                 checkForComodification();
    19                 throw new NoSuchElementException();
    20             }
    21         }
    22 
    23         public int nextIndex() {
    24             return cursor;
    25         }
    26 
    27         public int previousIndex() {
    28             return cursor-1;
    29         }
    30 
    31         public void set(E e) {
    32             if (lastRet < 0)
    33                 throw new IllegalStateException();
    34             checkForComodification();
    35 
    36             try {
    37                 AbstractList.this.set(lastRet, e);
    38                 expectedModCount = modCount;
    39             } catch (IndexOutOfBoundsException ex) {
    40                 throw new ConcurrentModificationException();
    41             }
    42         }
    43 
    44         public void add(E e) {
    45             checkForComodification();
    46 
    47             try {
    48                 int i = cursor;
    49                 AbstractList.this.add(i, e);
    50                 lastRet = -1;
    51                 cursor = i + 1;
    52                 expectedModCount = modCount;
    53             } catch (IndexOutOfBoundsException ex) {
    54                 throw new ConcurrentModificationException();
    55             }
    56         }
    57     }
    View Code

          其继承自Itr,实现了ListIterator,相比于Iterator只能单向遍历来说,ListIterator可以双向遍历,并且提供了add和set的方法,功能更为强大。对于无参数的listIterator来说,cursor的值为0 ,而带参数的方法指定了cursor的值。

          和remove()一样,当调用add()方法时,lastRet的值会变为-1,那这样就不能执行set和和remove了,而add方法实际上是可以执行多次的。另外add和set最终还是依赖于list的实现。

          其它新增的方法和原来的类似,只是说可以从另一端进行遍历。

           3. subList(beginIndex, endIndex) 返回了一个SubList对象。

           SubList实现了List的全部接口,但其对于list中各种方法的操作却依赖于构造函数中传递过来的list对象。所以,可以把SubList理解为是一个适配器。它和普通的列表并无两样,只是列表的开始位置不再是0,而是beginIndex, 列表的长度也不再是size(),而是endIndex - beginIndex.

      另外SubList并不是复制出一原列表的一部分,而是可以看成是原列表的一个视图,对这个子列表的所有修改,也就相当于是对原列表进修改,所以在做更新操作时,需要注意到这一点。

           最后需要注意的一点,就是subList是一个左闭右开的集合,比如list.subList(0,1)的子集合长度为1,其中包括元素list.get(0),不包括list.get(1)

    五、总结

         总的来说,List把Collection更具体化了,在加了一些限制的同时,也增加了更多的功能,主要就是按索引对元素进行操作。后续我们会围绕着List进行更深入的介绍。

          

           

  • 相关阅读:
    PHP中new static()与new self()的比较
    【程序员感悟系列】 由一点业务说开去
    配置管理工具 Puppet的安装和使用
    【读书笔记】大话设计模式
    博客还是要写起来 2016.08.13 周六
    linux启动SSH及开机自动启动
    Monkey日志信息的11种Event percentages
    Monkey官方帮助翻译&介绍
    Github问题An error occurred trying to download
    怎样克服拖延症,马上采取行动?
  • 原文地址:https://www.cnblogs.com/macs524/p/5724276.html
Copyright © 2011-2022 走看看