介绍

提供一种方法访问一个容器对象中各个元素,而不需暴露该对象的内部细节

迭代器模式有两个角色,一个是容器,一个是迭代器
由迭代器实现容器的遍历方法,容器通过调用迭代器的封装好的方法进行遍历
迭代其中通常有以下方法:

  • boolean hashNext():判断是否已经到达容器的边界。迭代器用游标来记录遍历节点数,通过与容器中的元素数量比较来避免越界
  • E next():获取下一个元素值。

或者是

  • boolean hashNext()
  • void next()
  • E currentItem

这两种方式有什么区别?以链表举例好了,如果是单项链表,适合第二种方式,如果使用方式一,在实现安全删除时,会漏掉第一个元素;如果是双向链表则都可以,java 中的 LinkedList 使用的是方式一

使用场景

减少遍历复杂数据结构的难度

模板

类图

迭代器模式 - 图1

JDK中的迭代器问题

image.png
如果遍历的时候,容器删除了元素,会有以下几种情况

  • 删除的是当前游标之后的元素,无影响
  • 删除的是当前游标之前的元素,容器中元素的位置就会向前挪

自实现容器使用foreach循环

思路

JDK 有个 foreach 语法糖
image.png
它实际上是在编译阶段转为了这个样子
image.png
如何使用这个语法糖呢?只需要实现这个接口就可以了
image.png
但是这里只是实现语法糖,如果要完全实现上面的效果,还需实现 Iterator 接口,实现 next 与 hasNext 方法

实现

这里的代码是我这篇链表与哨兵中的无哨兵链表实现。实现功能

  • 增加
  • 删除
  • 查找
  • foreach 循环
  • 安全删除

抽象类

  1. package cn.zjm404.stu.algorithm.list;
  2. import java.util.Iterator;
  3. /**
  4. * @author ZJM
  5. */
  6. public abstract class MyLinkedList<T> implements Iterable<T> {
  7. protected Node<T> head;
  8. protected Node<T> tail;
  9. @Override
  10. public Iterator<T> iterator() {
  11. return new MyLinkedIterator();
  12. }
  13. static class Node<T> {
  14. T value;
  15. Node<T> next;
  16. Node<T> prev;
  17. public Node(T t) {
  18. value = t;
  19. }
  20. }
  21. private class MyLinkedIterator implements Iterator<T> {
  22. private Node<T> curNode;
  23. private Node<T> lastReturn;
  24. public MyLinkedIterator() {
  25. curNode = head;
  26. }
  27. @Override
  28. public boolean hasNext() {
  29. return curNode != null;
  30. }
  31. @Override
  32. public T next() {
  33. lastReturn = curNode;
  34. curNode = curNode.next;
  35. return lastReturn.value;
  36. }
  37. public T currentItem(){
  38. return curNode.value;
  39. }
  40. @Override
  41. public void remove() {
  42. if(lastReturn == null){
  43. throw new IllegalStateException();
  44. }
  45. //孤立被删除节点
  46. Node<T> del = lastReturn;
  47. Node<T> delPrev = lastReturn.prev;
  48. //如果删除的节点为首节点
  49. if(delPrev == null){
  50. head = curNode;
  51. curNode.prev = null;
  52. }else{
  53. delPrev.next = del.next;
  54. del.next.prev = delPrev;
  55. }
  56. del.next = null;
  57. del.prev = null;
  58. lastReturn = null;
  59. }
  60. }
  61. public abstract void add(T t);
  62. public abstract void addFirst(T t);
  63. public abstract boolean removeFirst();
  64. public abstract boolean remove(T target);
  65. public abstract boolean contain(T target);
  66. }

无哨兵链表

  1. package cn.zjm404.stu.algorithm.list;
  2. public class NoSentryLinkedList<T> extends MyLinkedList<T>{
  3. @Override
  4. public void add(T t){
  5. if(head == null){
  6. tail = head = new Node<T>(t);
  7. return;
  8. }
  9. Node<T> newNode = new Node<T>(t);
  10. //建立联系
  11. newNode.prev = tail;
  12. tail.next = newNode;
  13. //挪动尾结点
  14. tail = newNode;
  15. }
  16. @Override
  17. public void addFirst(T t){
  18. if(head == null){
  19. tail = head = new Node<T>(t);
  20. return;
  21. }
  22. Node n = new Node(t);
  23. n.next = head;
  24. head.prev = n;
  25. head = n;
  26. }
  27. @Override
  28. public boolean removeFirst(){
  29. if(head == null){
  30. throw new NullPointerException("空链表异常!");
  31. }
  32. //如果链表中只有一个节点
  33. if(head == tail){
  34. head = tail = null;
  35. return true;
  36. }
  37. Node<T> del = head;
  38. //孤立节点
  39. if(del.next != null){
  40. del.next.prev = null;
  41. }
  42. head = del.next;
  43. //删除节点
  44. del.prev = null;
  45. del.next = null;
  46. return true;
  47. }
  48. @Override
  49. public boolean remove(T target){
  50. //如果为空链表,则直接返回
  51. if(head == null){
  52. return false;
  53. }
  54. //如果删除节点为头节点
  55. if(head.value.equals(target)){
  56. return removeFirst();
  57. }
  58. //遍历链表,找到后继节点为target的节点
  59. for(Node<T> n = head;n.next != null;n = n.next){
  60. if(n.next.value.equals(target)){
  61. //删除目标节点
  62. Node<T> del = n.next;
  63. //孤立节点
  64. n.next = del.next;
  65. if(del.next != null){
  66. del.next.prev = n;
  67. }
  68. //删除节点
  69. del.next = null;
  70. del.prev = null;
  71. //如果删除的节点为尾节点
  72. if(tail == del){
  73. tail = n.next;
  74. }
  75. return true;
  76. }
  77. }
  78. return false;
  79. }
  80. @Override
  81. public boolean contain(T target){
  82. for (Node<T> n = head; n != null; n = n.next){
  83. if(n.value.equals(target)){
  84. return true;
  85. }
  86. }
  87. return false;
  88. }
  89. }

测试

  1. package cn.zjm404.stu.algorithm.list;
  2. public class Client {
  3. public static void main(String[] args) {
  4. NoSentryLinkedList<String> nl = new NoSentryLinkedList<>();
  5. System.out.println("-------插入节点 测试---------------------");
  6. nl.add("hello");
  7. nl.add("world");
  8. nl.addFirst("WORLD");
  9. nl.addFirst("HELLO");
  10. for (String s : nl) {
  11. System.out.println(s);
  12. }
  13. System.out.println("-------删除 首节点 测试---------------------");
  14. nl.removeFirst();
  15. for (String s : nl) {
  16. System.out.println(s);
  17. }
  18. System.out.println("-------遍历删除 WORLD 测试---------------------");
  19. Iterator<String> iterator = nl.iterator();
  20. while(iterator.hasNext()){
  21. String cur = iterator.next();
  22. if(cur.equals("WORLD")){
  23. iterator.remove();
  24. continue;
  25. }
  26. System.out.println(cur);
  27. }
  28. System.out.println("-------当前状态---------------------");
  29. for (String s : nl) {
  30. System.out.println(s);
  31. }
  32. System.out.println("-------删除 world 测试---------------------");
  33. nl.remove("world");
  34. for (String s : nl) {
  35. System.out.println(s);
  36. }
  37. System.out.println("-------删除 最后一个节点 hello 测试---------------------");
  38. System.out.println("删除 hello 是否成功:"+nl.remove("hello"));
  39. System.out.println("查找 hello 是否存在:"+nl.contain("hello"));
  40. }
  41. }