容器---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();该类的源码如下:
 private class Itr implements Iterator<E> {
         /**
          * Index of element to be returned by subsequent call to next.
          */
         int cursor = 0;
         /**
          * 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.
          */
         int lastRet = -1;
         /**
          * 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;
         public boolean hasNext() {
             return cursor != size();
         }
         public E next() {
             checkForComodification();
             try {
                 int i = cursor;
                 E next = get(i);
                 lastRet = i;
                 cursor = i + 1;
                 return next;
             } catch (IndexOutOfBoundsException e) {
                 checkForComodification();
                 throw new NoSuchElementException();
             }
         }
         public void remove() {
             if (lastRet < 0)
                 throw new IllegalStateException();
             checkForComodification();
             try {
                 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();
         }
     }
通过分析源码,我们可以看到这是一个内部类,完整的实现了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对象,这个实现如下:
 private class ListItr extends Itr implements ListIterator<E> {
         ListItr(int index) {
             cursor = index;
         }
         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) {
             if (lastRet < 0)
                 throw new IllegalStateException();
             checkForComodification();
             try {
                 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);
                 lastRet = -1;
                 cursor = i + 1;
                 expectedModCount = modCount;
             } catch (IndexOutOfBoundsException ex) {
                 throw new ConcurrentModificationException();
             }
         }
     }
其继承自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进行更深入的介绍。
容器---List和AbstractList的更多相关文章
- Java容器解析系列(3) List AbstractList ListIterator RandomAccess fail-fast机制 详解
		做为数据结构学习的常规,肯定是先学习线性表,也就是Java中的List,开始 Java中List相关的类关系图如下: 此篇作为对Java中相关类的开篇.从上图中可以看出,List和AbstractLi ... 
- Java入门记(四):容器关系的梳理(上)——Collection
		目录 一.Collection及子类/接口容器继承关系 二.List 2.1 ArrayList 2.1.1 序列化的探讨 2.1.2 删除元素 2.1.3 调整大小 2.2 Vector和Stack ... 
- 转:java多线程--同步容器
		java同步容器 在Java的集合容器框架中,主要有四大类别:List.Set.Queue.Map.List.Set.Queue接口分别继承了Collection接口,Map本身是一个接口.注意Col ... 
- 给jdk写注释系列之jdk1.6容器(10)-Stack&Vector源码解析
		前面我们已经接触过几种数据结构了,有数组.链表.Hash表.红黑树(二叉查询树),今天再来看另外一种数据结构:栈. 什么是栈呢,我就不找它具体的定义了,直接举个例子,栈就相当于一个很窄的木桶 ... 
- 给jdk写注释系列之jdk1.6容器(3)-Iterator设计模式
		前面讲了两种List,一种基于数组实现的ArrayList,一种基于链表实现的LinkedList,这两种list是我们工作中最常用到的List容器.当然数组和链表也是两种常见的基本数据结构,其他基本 ... 
- 给jdk写注释系列之jdk1.6容器(2)-LinkedList源码解析
		LinkedList是基于链表结构的一种List,在分析LinkedList源码前有必要对链表结构进行说明. 1.链表的概念 链表是由一系列非连续的节点组成的存储结构,简单分下类的话,链 ... 
- 给jdk写注释系列之jdk1.6容器(1)-ArrayList源码解析
		工作中经常听到别人讲“容器”,各种各样的容器,话说到底什么是容器,通俗的讲“容器就是用来装东西的器皿,比如:水桶就是用来盛水的,水桶就是一个容器.” ok,在我们写程序的时候常常要对大量的对象进行管理 ... 
- JDK 高性能编程之容器
		高性能编程在对不同场景下对于容器的选择有着非常苛刻的条件,这里记录下前人总结的经验,并对源码进行调试 JDK高性能编程之容器 读书笔记内容部分来源书籍深入理解JVM.互联网等 先放一个类图util,点 ... 
- Java容器源码解析之——ArrayList
		public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess ... 
随机推荐
- PL/SQL Developer去掉启动时自动弹出的Logon弹出框方法
			以前用PL/SQL Developer 7.0版本,最近升级到PL/SQL Developer 11.0版本,但每次启动PL/SQL Developer都会自动弹出Logon窗口,并且选中其中的登录历 ... 
- SQL查询 - 表连接
			一.连接查询简介 连接查询中用来连接连个表的条件称为连接条件或连接谓词.其形式为: [<表1>].<列名1><连接运算符>[<表2>].<列2&g ... 
- HTTP协议学习
			面试过程中又一个常见的问题,http协议,因为做服务器开发如果用http协议的话,现在各种开源软件都封装好了,python中只需要简单的继承定义好的类,重写get或者post等方法,几行代码就可以搭建 ... 
- WebDriver基本API使用手册(基于Java和C#)
			WebDriver基本API使用手册(基于Java和C#) http://www.docin.com/p-747728551.html 
- python 集合set
			python的set和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素. 集合对象还支持union(联合), intersection(交), difference(差)和 ... 
- SQL Server 2008 R2 开启数据库远程连接
			今天要测试一个.net系统~因为配置的数据库是SQL Server~我就不得不安装SQL Server 2008 R2~现在我们就一起来看看SQL Server 2008 R2是如何打开远程连接端口1 ... 
- Nginx HTTP负载均衡和反向代理配置
			当前大并发的网站基本都采用了Nginx来做代理服务器,并且做缓存,来扛住大并发.先前也用nginx配置过简单的代理,今天有时间把整合过程拿出来和大家分享,不过其中大部分也是网上找来的资源. nginx ... 
- xcode6.0  模拟器打不开
			只要设置下这个就行了 本来以为虚拟机的问题 都把虚拟机从新安装了 杯具啊 
- 一种高效的 vector 四则运算处理方法
			实现 vector 的四则运算 这里假设 vector 的运算定义为对操作数 vector 中相同位置的元素进行运算,最后得到一个新的 vector.具体来说就是,假如 vector<int&g ... 
- zepto - slice
			var ss = ['1', '2', '3', '4', '5', '6']; console.log(ss.slice(2,4)); 
