前言:
使用ArrayList
优点:对于get和set的调用使用常数时间(即O(C))。
缺点:add和remove的代价昂贵(即O(N)),除非在末端进行。
迭代器位于需要被删除的节点上,其删除方法也是昂贵的,因为数组的项必须要移动。
使用LinkedList:
优点:add和remove开销很小(即O(C),假设变动项位置已知)。
缺点:不易做索引,因此get()的代价是昂贵的(即O(N),如果靠近后端可以从后端开始)。
1.MyArrayList类:可增长数组
值得注意的一些细节:
提供一个将保持基础数据组,数组的容量,以及存储在MyArrayList中的当前项数。ensureCapacity()方法。新容量小于已有容量的请求被忽略,等于相当于收缩数组,大于将成倍增加数组容量,并将老数据拷贝到新数组中,旧数组等待被回收。
add和remove操作在时间上开销是昂贵的,操作之后还要将数组其他数据移位。
提供一个实现Iterator接口的类。
以下是代码实现:
该实现未做对迭代器无效的结构上的修改,也未检测非法的迭代器remove方法。会在MyLinkedList中实现。
5:有效数据量
6:数组实际存储数据
59:+1是用于大小是0的情况
82:current代表被查看的下一项的下标
87~92:next()方法的意思是:取出当前current所指数据,然后将current加加。
94~96:删除之前走过的节点(即之前调用了next()方法,current被++了,因此这里要删除被走过的节点就要--),然后后面的数据向前移位,current就又指向了新的节点。
重点看迭代器的实现,其基本思路是使用一个内部类返回一个ArrayListIterator实例。
其中关于迭代器的一些相关问题可参见嵌套类与内部类。
2.MyLinkedList类:双链表
需要注意的一些细节:
MyLinkedList类本身,包含到两端的链(头节点,尾节点),其他方法等。
Node类,私有嵌套类。
LinkedListIterator类,实现Iterator接口。
以下是实现代码:
22~28:set()方法中 p是对Node的对象引用,因此这样是可以直接修改链表中节点值的。
45:modCount用来帮助迭代器检测集合中的变化。代表自从构造以来对链表所做的改变的次数。每次对add或remove的调用都将更新modCount。
当一个迭代器被创建时,它将存储结合的modCount,每次对一个迭代器方法(next或remove)的调用都将用该链表内的当前modCount检测在迭代器内存储的modCount,并且当这两个计数不匹配是抛出一个ConcurrentModificationException异常。
其中Node类实现如下:
addBefore例程:
注意要严格按照1,2,3,4的顺序插入。
因为有BeginMaker的存在使得任意的插入都是在两个节点之间进行。
删除节点方法:
getNode方法:
迭代器方法:
合并了重要的错误检测功能。
4:expectedModCount:注意第4行,expectedModCount迭代器内部存储的修改次数。呼应之前。
5 okToRemove:只有在next()方法中置true,remove之后仍旧置false,因此确保了只有next()被调用后才可以使用remove()方法(当然是LinkedListIterator.remove());
10~21 next()方法:同样的除了错误检测外,主要的思路仍旧是取出current所指的项,然后current“加加”。
23~34 remove()方法:第30行,删除迭代器遍历过的一项,因此当然是MyLinkedList.this.remove(current.prev);current正在观察的节点不受前面节点被删除的影响,仍然指向调用next()方法后所指向的那一节点。
32:注意,这里的remove操作也要记录,也就是使expectedModCount++;