Java数据结构二 —— MyArrayList和MyLinkedList的实现

前言:

  使用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++;

 

你可能感兴趣的:(LinkedList)