按链表的组织形式分有ArrayList和LinkList两种。ArrayList内部其实是用数组的形式实现链表,比较适合链表大小确定或较少对链表进行增删操作的情况,同时对每个链表节点的访问时间都是constant;而LinkList内部以一个List实现链表,比较适合需要频繁对链表进行操作的情况,对链表节点的访问时间与链表长度有关O(N)。
另外,根据实现形式可以分为直接式(想不出什么合适的名字,姑且这样吧)和使用Iterator(迭代模式)两种方法。直接式的实现方法和C/C++中的写法差不多;而使用Iterator时,需要实现java.lan中的Iterable接口(或者也可以自己在链表内部定义自己的Iterator method)
使用迭代模式的优点:
1,实现功能分离,简化容器接口。让容器只实现本身的基本功能,把迭代功能委让给外部类实现,符合类的设计原则。
2,隐藏容器的实现细节。
3,为容器或其子容器提供了一个统一接口,一方面方便调用;另一方面使得调用者不必关注迭代器的实现细节。
4,可以为容器或其子容器实现不同的迭代方法或多个迭代方法。
我觉得第4点说的很好,对于一堆数据而言,不同的人(或业务逻辑)使用它的方式也不尽相同,定义一个theIterator继承Iterator,不仅提供next,hasNext 以及remove这个最小的操作集合,同时也可以提供更多的其它方法。在theIterator的实现类中,具体实现不同的迭代方法,比如顺序、逆序或根据其它语义进行遍历等,再通过类厂的方式将一个theIterator实现的对象交给用户使用。
下面我给出两个例子:
首先是直接式实现链表的例子,这个例子只提供了几种链表操作的基本方法,仅用于示意:
- public class MyList<AnyType> {
- private class Node<AnyType>{
- public Node<AnyType> pre;
- public Node<AnyType> next;
- public AnyType data;
- public Node(AnyType d, Node<AnyType>p, Node<AnyType> n){}
- public Node(){}
- }
- private int theSize;
- private Node<AnyType> Header;
- private Node<AnyType> Tail;
- public MyList(){}
- public void add(AnyType item){}
- public boolean isEmpty(){}
- public int size(){}
- public AnyType get( int idx){}
- public void print(){}
- }
Node<AnyType>类定义了双向链表中节点的结构,它是一个私有类,而其属性和构造函数都是公有的,这样,其父类可以直接访问其属性,而外部类根本不知道Node类的存在。Data是节点中的数据与,pre指向前一个Node节点,next指向后一个Node节点。其构造函数的实现如下,不解释:
- public Node(AnyType d, Node<AnyType>p, Node<AnyType> n){
- this.data = d;
- this.pre = p;
- this.next = n;
- }
- public Node(){
- this.data = null;
- this.pre = null;
- this.next = null;
- }
下面我们看一下链表的构造函数实现:
- public MyList(){
- theSize = 0;
- Header = new Node<AnyType>(null,null,null);
- Tail = new Node<AnyType>(null,Header,null);
- Header.next = Tail;
- }
我们构造了一个带有头、尾节点的双向链表,头节点的Next指向尾节点,为节点的pre指向头节点。链表长度起始为0。
继续贴上链表类其它方法的实现,不解释了,应该比较清楚:
- public void add(AnyType item){
- Node<AnyType> aNode = new Node<AnyType>(item,null,null);
- Tail.pre.next = aNode;
- aNode.pre = Tail.pre;
- aNode.next = Tail;
- Tail.pre = aNode;
- theSize++;
- }
- public boolean isEmpty(){
- return ( theSize == 0);
- }
- public int size(){
- return theSize;
- }
- public AnyType get( int idx){
- if(idx > theSize-1 || idx < 0)
- throw new IndexOutOfBoundsException();
- Node<AnyType> current = new Node<AnyType>(null,Header,null);
- for(int i = 0; i<idx; i++)
- current = current.next;
- return current.data;
- }
- public void print(){
- Node<AnyType> current = Header.next;
- while(current.next != null){
- //如果AnyType是你自己定义 //的数据类型,那么请务必提供
- //一个toString方法,要么就不
- //要在链表里实现print方法。
- System.out.println(current.data.toString());
- current = current.next;
- }
- }
第二个例子是用迭代方法实现链表的例子,下面是类定义:
- public class MyListItr<Type> implements Iterable<Type> {
- private class Node<Type>{
- public Type data;
- public Node<Type> pre;
- public Node<Type> next;
- public Node(){}
- public Node(Type d, Node<Type> p, Node<Type> n){}
- }
- private Node<Type> Header;
- private Node<Type> Tail;
- private int theSize;
- public MyListItr(){}
- public void add(Type item){}
- public void print(){}
- public int size(){}
- @Override
- public Iterator<Type> iterator() {}
- private class MyListIterator implements Iterator<Type>{
- @Override
- public boolean hasNext() {}
- @Override
- public Type next() {}
- @Override
- public void remove() {}
- }
- }
这里主要说一下与前面例子不同的地方:MyListItr类实现了Java.lan.Itrable接口,因此我们必须实现其public Iterator<Type> iterator() {}方法,它的返回值是一个迭代器对象,那么我就定义一个内部私有类——MyListIterator,这个类实现了iterator接口。在public Iterator<Type> iterator() 方法中,我就构造这么一个MyListIterator的对象并返回。
Iterator接口有三个方法是我们必须实现的,hasNext,next和remove,这是迭代操作的最小集合了。对于不同的迭代器执行方式,我们可以定义多个类似MyListIterator这样的实现类,只要在链表类中的iterator() 方法中把具体实现类的对象返回给用户就OK了。
罗嗦两句:我感觉如果我们定义链表类时,如果不继承Iterable接口,而是直接在类内部提供 public theIterator Iterator() 方法,theIterator是一个自定义接口,继承自Iterator接口,这样我们可以提供遵循theIterator接口的各种迭代方式的不同实现类,并通过一个类厂方法在链表的public theIterator Iterator() 方法中把具体迭代实现类的对象交给用户,以此就可以根据不同的需求,提供链表的多种迭代方式。我觉得这种方法是可行的,但并没有具体实验。
下面只贴出链表iterator()方法和迭代实现类的MyListIterator代码,其它方法就不重复贴了:
- @Override
- public Iterator<Type> iterator() {
- return new MyListIterator();
- }
- private class MyListIterator implements Iterator<Type>{
- Node<Type> current = Header.next;
- @Override
- public boolean hasNext() {
- return (current != Tail);
- }
- @Override
- public Type next() {
- if(!hasNext())
- throw new IndexOutOfBoundsException();
- Type item = current.data;
- current = current.next;
- return item;
- }
- @Override
- public void remove() {
- if( !hasNext())
- throw new NoSuchElementException();
- current.pre.next = current.next;
- current.next.pre = current.pre;
- current = current.next;
- theSize--;
- }
- }