Java数据结构和算法-链表(5-迭代器)

在链表中可以通过find()方法来查找到某一个特定的值,也可以通过delete方法删除某一个特定的值。这些操作都有一个搜索的工作,以找到特定的链接点。
在链表中进行一次某个值的查找删除或插入是简单的。但是现实中,经常会进行的是对某些特定的链接点的操作,而这些操作也都要进行搜索的工作。例如:要查找出所有某班成绩低于平均成绩的同学,要改变公司所有拥有最低工资的员工工资。这些都要进行搜索。
但是链表不是数组,链表没有下标,如果是数组的话,可以通过跟踪下标来确定当前搜索到的位置,当查找到第一个符合条件的值的同时,返回当前的下标,从而使得在继续查找下一个符合条件的值时,可以从该下标开始。链表不可以,链表在返回了符合条件的值后,必须从头开始查找下一个符合条件的值。如果有一个保存当前链接点的引用,在下一次查找时,从该引用的链接点进行步进,这样效率就会提高很多。
可能有人在说到“保存当前链接点的引用”时,就想到了在之前链表的介绍中,在链表类中定义的current,用current来存取一个链接点,然后使current递增,来移动到下一个链接点。
这种方法存在的一个问题是,只能进行一种引用。但是在现实中可能不止一个,就像经常要使用多个下标。并且需要引用的数目是未知的,所以这个引用不应该定义在链表内,而应该封装在一个独立的类中,并且这个类不能和链表相同。

迭代器

迭代器类是封装了对数据结构的数据项的引用的类,并用来遍历这些对象。在介绍的初始定义如下:

class ListIterator() {
    private Link current;

    //...
}

它包含了一个对链接点的引用。
在产生链表后,可以通过调用某些方法来创建迭代器,比如下面的操作:

public static void main(String[] args) {

    LinkList linkList = new LinkList();
    ListIterator iter1 = linkList.getIterator();
    Link link = iter1.getCurrent();
    iter1.nextLink();
}

在上面的代码中通过getIterator()来获取链表的迭代器,创建这个迭代器之后,可以通过它的递增,来指向下一个链接点。当然迭代器可以不止一个,迭代器总是指向链表中的一些链接点,它通过引用与链表相关联,但不等同于它就是链表或者链接点,如下图:
Java数据结构和算法-链表(5-迭代器)_第1张图片

迭代器的其他特性

在链表章节的其他程序中,都会定义一个previous的字段,这使得链表进行删除或者插入操作时更加便捷,因此在迭代器中也封装了这个字段。
另外,在链表操作中我们可能会在表头进行一下操作,所以访问first也是必要的,但是first是链表的私有字段,迭代器要如何访问这个字段呢?
一种方式是在链表创建迭代器时,向迭代器传递一个对first的引用给迭代器,然后将这个引用存贮在迭代器的某个字段中。
还有一种方式是在链表中设一个公有方法getFirst来获取first的引用。不过这种方式有一个弊端是允许任何人修改first,这是危险的。

迭代器的方法

我们将链表中的部分方法也通过迭代器来实现,这样可以使得迭代器更加灵活和强大,例如迭代器包含以下的方法:

  • reset()–把迭代器设在表头
  • nextLink()–将迭代器移动到下一个链接点
  • getCurrent()–获取当前迭代器指向的链接点
  • insertAfter()–在迭代器指向的链接点之后插入
  • insertBefore()–在迭代器指向的链接点之前插入
  • atEnd()–判断迭代器是否在表尾
  • deleteCurrent()–删除当前迭代器指向的链接点

哪些任务由链表完成,哪些由迭代器完成,这并不好决定。insertFirst的插入位置是固定的不需要搜索所以将这个方法由链表来实现,而insertBefore这需要搜索工作,所以就有迭代器来实现。displayList可以通过链表也可以通过迭代器来实现,不过在下面的介绍中,我们通过链表实现。

迭代器的Java代码

class Link {
    private int iData;
    private Link next;

    public Link(int d) {
        iData = d;
    }

    public int getIData() {
        return iData;
    }

    public void setIData(int d) {
        iData = d;
    }

    public void displayLink() {
        System.out.println("Link: " + iData);
    }

    //next的get和set方法
}//end Link

class LinkList {
    private Link first;

    public ListIterator getIterator() {
        return new ListIterator(this);
    }

    public Link getFirst() {
        return first;
    }

    public void setFirst(Link l) {
        first = l;
    }

    public void insertFirst(int d) {
        Link newLink = new Link(d);

        newLink.next = first;
        first = newLink;    
    }

    public boolean isEmpty() {
        return (first == null);
    }

    public void displayList() {
        Link current = first;
        while(current != null) {
            current.displayLink();
        }
    }
}//end LinkList

class ListIterator {
    private Link previous;
    private Link current;
    private LinkList myList;

    public ListIterator(LinkList ll) {
        myList = ll;
        reset();
    }

    public void reset() {
        current = myList.getFirst();
        previous = null;
    }

    public boolean atEnd() {
        return (current.getNext() == null);
    }

    public void nextLink() {
        if(atEnd()) {
            reset();
        } else {
            previous = current;
            current = current.getNext();
        }
    }

    public void insertAfter(int d) {
        Link newLink = new Link(d);

        if(isEmpty()) {
            myList.setFirst(newLink);
            current = newLink;
        } else {
            newLink.setNext(current.getNext());
            current.setNext(newLink);
            nextLink();
        }
    }

    public void insertBefore(int d) {
        Link newLink = new Link(d);

        if(previous == null) {
            myList.setFirst(newLink);
            reset();
        } else {
            newLink.setNext(current);
            previous.setNext(newLink);
            current = newLink;
        }
    }

    public int deleteCurrent() {
        Link temp = current;
        if(previous == null) {
            myList.setFirst(current.next);
            reset();
        } else {
            previous.setNext(current.getNext());
            if(atEnd())
                reset();
            else
                current = current.getNext();
        }
        return temp.getIData();
    }

    public int getCurrent() {
        return current.getIData();
    }
}//end ListIterator 

class IteratorApp {
    public static void main(String[] args) {
        LinkList linkList = new LinkList();
        ListIterator ite = linkList.getIterator();

        ite.insertAfter(21);
        ite.insertAfter(14);
        ite.insertAfert(45);
        ite.insertAfter(30);
        ite.insertAfter(67);

        ite.reset();
        if(ite.getCurrent()%3 == 0)
            ite.deleteCurrent();

        while(!ite.atEnd()) {
            ite.nextLink();

            if(ite.getCurrent()%3 == 0)
                ite.deleteCurrent();
        }
        linkList.
    }
}

在上面的代码中,deleteCurrent方法当删除了当前所指向的链接点之后,current可能指向下一个,上一个或者回到表头,因为在删除链接点之后,迭代器仍指向附近的节点,能够便于下一个操作,而且如果指向上一个链接点,由于不是双向链表,链接点没有关于previous的引用,所以指向上一个链接点会比较复杂,因此deleteCurrent最后current会指向下一个链接点。

你可能感兴趣的:(Java数据结构和算)