一、前言

  前面我们介绍了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的更多相关文章

  1. Java容器解析系列(3) List AbstractList ListIterator RandomAccess fail-fast机制 详解

    做为数据结构学习的常规,肯定是先学习线性表,也就是Java中的List,开始 Java中List相关的类关系图如下: 此篇作为对Java中相关类的开篇.从上图中可以看出,List和AbstractLi ...

  2. Java入门记(四):容器关系的梳理(上)——Collection

    目录 一.Collection及子类/接口容器继承关系 二.List 2.1 ArrayList 2.1.1 序列化的探讨 2.1.2 删除元素 2.1.3 调整大小 2.2 Vector和Stack ...

  3. 转:java多线程--同步容器

    java同步容器 在Java的集合容器框架中,主要有四大类别:List.Set.Queue.Map.List.Set.Queue接口分别继承了Collection接口,Map本身是一个接口.注意Col ...

  4. 给jdk写注释系列之jdk1.6容器(10)-Stack&Vector源码解析

    前面我们已经接触过几种数据结构了,有数组.链表.Hash表.红黑树(二叉查询树),今天再来看另外一种数据结构:栈.      什么是栈呢,我就不找它具体的定义了,直接举个例子,栈就相当于一个很窄的木桶 ...

  5. 给jdk写注释系列之jdk1.6容器(3)-Iterator设计模式

    前面讲了两种List,一种基于数组实现的ArrayList,一种基于链表实现的LinkedList,这两种list是我们工作中最常用到的List容器.当然数组和链表也是两种常见的基本数据结构,其他基本 ...

  6. 给jdk写注释系列之jdk1.6容器(2)-LinkedList源码解析

    LinkedList是基于链表结构的一种List,在分析LinkedList源码前有必要对链表结构进行说明.   1.链表的概念      链表是由一系列非连续的节点组成的存储结构,简单分下类的话,链 ...

  7. 给jdk写注释系列之jdk1.6容器(1)-ArrayList源码解析

    工作中经常听到别人讲“容器”,各种各样的容器,话说到底什么是容器,通俗的讲“容器就是用来装东西的器皿,比如:水桶就是用来盛水的,水桶就是一个容器.” ok,在我们写程序的时候常常要对大量的对象进行管理 ...

  8. JDK 高性能编程之容器

    高性能编程在对不同场景下对于容器的选择有着非常苛刻的条件,这里记录下前人总结的经验,并对源码进行调试 JDK高性能编程之容器 读书笔记内容部分来源书籍深入理解JVM.互联网等 先放一个类图util,点 ...

  9. Java容器源码解析之——ArrayList

    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess ...

随机推荐

  1. gtest 安装

    1.下载,https://code.google.com/p/googletest/.解压,进入该目录,按REAME说明 安装:1.5之前 make install可以安装,1.6之后不可以...安装 ...

  2. php安装phalcon扩展

    一.关于phalcon: 简介: Phalcon 是开源.全功能栈.使用 C /zephir 编写.针对高性能优化的 PHP 5 框架. 开发者不需要学习和使用 C 语言的功能, 因为所有的功能都以 ...

  3. 调用 webapi的put和delete 报"Method Not Allowed" 405 错误。

    修改引用到webapi的Dll文件对应的项目的web.config 选择生成读写方法webapi会生成四个读写的方法(CRUD),两个获取数据的.一个更新.一个删除,默认情况下更新和删除是不对外开外的 ...

  4. Cubieboard2裸机开发之(三)C语言操作LED

    前言 前面通过汇编语言点亮LED,代码虽然简单,但并不是很直观.这次使用熟悉的C语言来控制LED,但是需要注意的地方有两点,第一,要想使用C语言,首先需要在调用C语言代码之前设置好堆栈:第二,调用C语 ...

  5. Windows下Git安装指南

    参考<Git权威指南>安装整理,图书配套网址参见[1] 1. Cygwin下安装配置Git 1. 在Windows下安装配置Git有2种不同的方案 (1)msysGit, (2)Cygwi ...

  6. Windows Driver Foundation-User-Mode Driver Framework 服务不能启动(错误31)问题解决

    这个错误是由于WudfPf这个服务没有启动有关,导致开机时出现SVCHOST.EXE出现,内存不能"Written"的错误, http://answers.yahoo.com/qu ...

  7. 数据库时间createtime字段 数据类型的选取

    之前是一直在用datetime类型(db)精度到yyyy-MM-dd HH:mm:ss 或者 date类型 精度到 yyyy-MM-dd 用了框架自动注入功能,自己也没去深入没去管他的set赋值值等等 ...

  8. 对象与Byte数组相互转化工具方法

    /** * 对象转byte * @param obj * @return */ private byte[] ObjectToByte(Object obj) { byte[] bytes = nul ...

  9. [linux]查看linux下端口占用

    netstat netstat -an | grep 23 (查看是否打开23端口) 查看端口占用情况的命令:lsof -i [root@www ~]# lsof -i COMMAND PID USE ...

  10. Android 学习笔记之使用多线程实现断点下载...

    PS:莫名其妙的迷茫... 学习内容: 1.使用多线程实现文件下载...   多线程下载是加快下载速度的一种方式..通过开启多个线程去执行一个任务..可以使任务的执行速度变快..多线程的任务下载时常都 ...