我们来实现一个双向链表:
//node
public class Node {
public Object data;
public Node next;
public Node prev;
public Node(Object data){
this.data = data;
}
}
//双向链表
public class TwoWayLinkedList {
private Node head;//表示链表头
private Node tail;//表示链表尾
private int size;//表示链表的节点个数
public TwoWayLinkedList(){
head = null;
current = null;
tail = null;
}
//在链表头增加节点
public void addHead(Node newNode){
if(size == 0){
head = newNode;
tail = newNode;
size++;
}else{
head.prev = newNode;
newNode.next = head;
head = newNode;
size++;
}
}
//在链表尾增加节点
public void addTail(Node newNode){
if(size == 0){
head = newNode;
tail = newNode;
size++;
}else{
newNode.prev = tail;
tail.next = newNode;
tail = newNode;
size++;
}
}
//删除链表头
public Node deleteHead(){
Node temp = head;
if(size != 0){
head = head.next;
head.prev = null;
size--;
}
return temp;
}
//删除链表尾
public Node deleteTail(){
Node temp = tail;
if(size != 0){
tail = tail.prev;
tail.next = null;
size--;
}
return temp;
}
//获得链表的节点个数
public int getSize(){
return size;
}
//判断链表是否为空
public boolean isEmpty(){
return (size == 0);
}
}
我想在应用场景中从前到后遍历出这个双向列表中的每一个结点进行操作,那么好,我们可以在链表类中加入一个当前索引,并实现first,next,isdone等函数,代码如下:
public class TwoWayLinkedList {
private Node head;//表示链表头
private Node tail;//表示链表尾
private int size;//表示链表的节点个数
private Node current;
public TwoWayLinkedList(){
size = 0;
head = null;
current = null;
tail = null;
}
//在链表头增加节点
public void addHead(Node newNode){
if(size == 0){
head = newNode;
tail = newNode;
size++;
}else{
head.prev = newNode;
newNode.next = head;
head = newNode;
size++;
}
current = head;
}
//在链表尾增加节点
public void addTail(Node newNode){
if(size == 0){
head = newNode;
tail = newNode;
size++;
}else{
newNode.prev = tail;
tail.next = newNode;
tail = newNode;
size++;
}
current = head;
}
//删除链表头
public Node deleteHead(){
Node temp = head;
if(size != 0){
head = head.next;
head.prev = null;
size--;
}
current = head;
return temp;
}
//删除链表尾
public Node deleteTail(){
Node temp = tail;
if(size != 0){
tail = tail.prev;
tail.next = null;
size--;
}
return temp;
}
//获得链表的节点个数
public int getSize(){
return size;
}
//判断链表是否为空
public boolean isEmpty(){
return (size == 0);
}
public Node first()
{
current = head;
return head;
}
public Node next()
{
Node tmp = current.next;
if(current.next != null)
{
current = current.next;
}
return tmp;
}
public boolean isDone()
{
return (current.next == null);
}
}
应用场景:
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
Node node4 = new Node(4);
Node node5 = new Node(5);
Node node6 = new Node(6);
Node node7 = new Node(7);
TwoWayLinkedList list = new TwoWayLinkedList();
list.addTail(node1);
list.addTail(node2);
list.addTail(node3);
list.addTail(node4);
list.addTail(node5);
list.addTail(node6);
list.addTail(node7);
Node i=list.first();
while(i != null)
{
System.out.println(i.data);
i = i.Next();
}
}
}
此时我想加一种遍历方法,我想从后向前遍历这个链表中的所有元素,那么通常操作就是在TwoWayLinkedList 链表类中添加一套逆向遍历操作接口,在这个场景中也是可行的,但是我们设想一个集合,我们的需求对其遍历的方式很多,并且后期可能还会增加或修改,这个时候出于开闭原则的考虑,我们要将遍历方式的变化进行封装,使得遍历方式的变化对链表类没有影响,对链表和遍历操作进行解耦,这个时候我们就用到了迭代模式。
迭代模式:提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
一、特征
1、顺序访问:即,此模式一般是伴生于集合的,它提供了对一种集合不同遍历方式的扩展,也提供了对多种集合统一遍历方式的方案。
2、不暴露该对象的内部表示:即,迭代模式的实现尽量不能影响集合的封装特性,这在c++中可以用友元类实现,在java中可以使用内部类实现。
二、作用:
某些应用场景需要对一个集合进行多种形式的遍历,并且遍历方式可能会发生改变。
三、实现:
我们来看看双向链表的遍历如何使用迭代模式:
//集合接口
public interface Aggregate {
public Iterator CreateIterator();
}
//迭代器接口
public interface Iterator {
public Node First();
public Node Next();
public boolean IsDone();
}
//具体集合,双向链表
public class ConcreteAggregate implements Aggregate{
public Node head;//表示链表头
public Node tail;//表示链表尾
private int size;//表示链表的节点个数
public ConcreteAggregate(){
size = 0;
head = null;
tail = null;
}
public Iterator CreateIterator()
{
PreIterator ite = new PreIterator(this);
return ite;
}
//在链表头增加节点
public void addHead(Node newNode){
if(size == 0){
head = newNode;
tail = newNode;
size++;
}else{
head.prev = newNode;
newNode.next = head;
head = newNode;
size++;
}
}
//在链表尾增加节点
public void addTail(Node newNode){
if(size == 0){
head = newNode;
tail = newNode;
size++;
}else{
newNode.prev = tail;
tail.next = newNode;
tail = newNode;
size++;
}
}
//删除链表头
public Node deleteHead(){
Node temp = head;
if(size != 0){
head = head.next;
head.prev = null;
size--;
}
return temp;
}
//删除链表尾
public Node deleteTail(){
Node temp = tail;
if(size != 0){
tail = tail.prev;
tail.next = null;
size--;
}
return temp;
}
//获得链表的节点个数
public int getSize(){
return size;
}
//判断链表是否为空
public boolean isEmpty(){
return (size == 0);
}
}
//具体迭代器,双向链表顺序迭代器
public class PreIterator implements Iterator{
private ConcreteAggregate mylist;
private Node current;
public PreIterator(ConcreteAggregate aggregate)
{
this.mylist = aggregate;
current = null;
}
public Node First() {
current = mylist.head;
return current;
}
public Node Next() {
Node tmp = current.next;
if(current.next != null)
{
current = current.next;
}
return tmp;
}
public boolean IsDone() {
return (current.next == null);
}
}
//具体迭代器,双向链表倒序迭代器
public class TailIterator implements Iterator{
private ConcreteAggregate mylist;
private Node current;
public TailIterator(ConcreteAggregate aggregate)
{
this.mylist = aggregate;
current = null;
}
public Node First() {
current = mylist.tail;
return current;
}
public Node Next() {
Node tmp = current.prev;
if(current.prev != null)
{
current = current.prev;
}
return tmp;
}
public boolean IsDone() {
return (current.prev == null);
}
}
//应用场景
public class Client {
public static void main(String[] args) {
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
Node node4 = new Node(4);
Node node5 = new Node(5);
Node node6 = new Node(6);
Node node7 = new Node(7);
ConcreteAggregate list = new ConcreteAggregate();
list.addTail(node1);
list.addTail(node2);
list.addTail(node3);
list.addTail(node4);
list.addTail(node5);
list.addTail(node6);
list.addTail(node7);
// Iterator ite = new PreIterator(list);
Iterator ite = new TailIterator(list);
Node i=ite.First();
while(i != null)
{
System.out.println(i.data);
i = ite.Next();
}
}
}
四、缺点
由于高级语言对于通用集合都实现了各种相关遍历,并且这些遍历方式就是迭代模式的实现,所以简单的通用集合不必使用迭代模式。
迭代器需要访问具体集合的数据,所以可能会影响到集合的数据封装,所以要考虑迭代器的实现方案。