天天看點

23種設計模式----疊代器模式----行為模式

文章目錄

  • ​​1.疊代器模式目的​​
  • ​​2.疊代器模式實作​​
  • ​​2.1一些名詞​​
  • ​​疊代器:進行周遊行為的類​​
  • ​​容器:存放元素的類​​
  • ​​2.3實作類定義​​
  • ​​2.4 實體類定義​​
  • ​​2.5 測試類定義​​
  • ​​2.6 測試結果​​
  • ​​3.疊代器模式擴充​​
  • ​​java中util包中的​​
  • ​​至于容器的接口​​
  • ​​3.2例子​​
  • ​​3.2.1學校(容器)​​
  • ​​3.2.2學生(實體)​​
  • ​​3.2.3學校的疊代器​​
  • ​​3.2.4測試代碼​​
  • ​​3.3實作自己的集合​​
  • ​​4.常見的問題​​
  • ​​4.1在周遊的過程中删除​​
  • ​​4.2周遊的并發安全性​​

23種設計模式

#疊代器模式

1.疊代器模式目的

  疊代器模式主要實作行為的分離。

實作周遊行為的分離。

2.疊代器模式實作

2.1一些名詞

疊代器接口:一個接口,定義hasNext和next方法。

容器接口:一個接口,定義iterator方法。

疊代器:實作了疊代器接口的類。

容器:實作了容器接口的類。

hasNext:判斷next方法是否可用

next:傳回目前對象,并指向下一個

iterator:傳回目前容器的疊代器

remove:删除目前元素

疊代器:進行周遊行為的類

容器:存放元素的類

###2.2接口的定義

疊代器接口

public interface MyIterator {

  boolean hasNext();
  
  People next();
  
}      

容器接口

public interface Container {

  RoomEach iterator();
  
}      

2.3實作類定義

疊代器

public class RoomEach implements MyIterator{

  private Room room;
  
  private int index;
  
  public RoomEach(Room room) {
    this.room = room;
    index = 0;
  }
  
  @Override
  public boolean hasNext() {
    if(index < room.getSize())
      return true;
    else
      return false;
  }

  @Override
  public People next() {
    People people = room.getPeople(index);
    index++;
    return people;
  }

}      

容器

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class Room implements Container{

  private List<People> roomPeople;
  
  public Room(int size){
    roomPeople = new ArrayList<People>();
    for(int i = 0 ;i < size;i++){
      People people = new People();
      people.setName(new SimpleDateFormat("hh:mm:ss").format(new Date()).toString());
      people.setAge(Integer.valueOf(System.currentTimeMillis()%1000+""));
      roomPeople.add(people);
    }
  }
  
  public int getSize(){
    return this.roomPeople.size();
  }
  
  public People getPeople(int i){
    return roomPeople.get(i);
  }
  
  @Override
  public RoomEach iterator() {
    return new RoomEach(this);
  }

}      

2.4 實體類定義

元素

import java.text.SimpleDateFormat;
import java.util.Date;

public class People {

  private String name;
  
  private int age;
  
  public People(){
    this.name = new SimpleDateFormat("hh:mm:ss").format(new Date()).toString();
    this.age = Integer.valueOf(System.currentTimeMillis()%1000 + "");
  }
  
  public void setName(String name){
    this.name = name;
  }
  
  public void setAge(int age){
    this.age = age;
  }
  
  public String getName(){
    return this.name;
  }
  
  public int getAge(){
    return this.age;
  }
  
}      

2.5 測試類定義

測試

public class Main {

  public static void main(String[] args) {
    
    Room room = new Room(10);
    
    RoomEach roomEach = room.iterator();
    while(roomEach.hasNext()){
      People people = roomEach.next();
      System.out.println(people.getName()+"|||||||"+people.getAge());
    }
    
  }

}      

2.6 測試結果

04:22:15|||||||980
04:22:15|||||||980
04:22:15|||||||980
04:22:15|||||||981
04:22:15|||||||981
04:22:15|||||||981
04:22:15|||||||981
04:22:15|||||||981
04:22:15|||||||981
04:22:15|||||||981      

3.疊代器模式擴充

###3.1疊代器模式的接口可以使用jdk的接口

java中util包中的

public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}      

一般remove方法很少被用到,主要還是前兩個方法。

是以呢,要實作疊代器需要實作疊代器的接口

至于容器的接口

java.lang包

import java.util.Iterator;
public interface Iterable<T> {
    Iterator<T> iterator();
}      

是以,如果你隻需要疊代器最普通的實作,那麼隻需要在對應的類上實作對應的接口就行。

3.2例子

3.2.1學校(容器)
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class School implements Iterable<Student>{

  private List<Student> students;
  
  public School(int n) {
    students = new ArrayList<Student>();
    for(int i = 0;i < n;i++){
    Student student = new Student();
    student.setName(i+"");
    student.setAge(i);
    student.setSubject(i+"");
    students.add(student);
    }
  }
  
  public int getLength() {
    return students.size();
  }
  
  public Student getStudent(int x){
    return students.get(x);
  }
  
  public void removeStudent(int x){
    students.remove(x);
  }
  
  @Override
  public Iterator<Student> iterator() {
    return new SchoolQuery(this);
  }

}      
3.2.2學生(實體)
public class Student {

  private String name;
  
  private int age;
  
  private String subject;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }

  public String getSubject() {
    return subject;
  }

  public void setSubject(String subject) {
    this.subject = subject;
  }
}      
3.2.3學校的疊代器
import java.util.Iterator;

public class SchoolQuery implements Iterator<Student>{

  private School school;
  
  private int index;
  
  public SchoolQuery(School school) {
    this.school = school;
    index = 0;
  }
  
  @Override
  public boolean hasNext() {
    if(index < school.getLength())
      return true;
    else
      return false;
  }

  @Override
  public Student next() {
    Student student = school.getStudent(index);
    index++;
    return student;
  }

  @Override
  public void remove() {
    school.removeStudent(index - 1);
  }

}      
3.2.4測試代碼
School school = new School(50);
    SchoolQuery schoolQuery = (SchoolQuery) school.iterator();
    while (schoolQuery.hasNext()) {
      Student student = schoolQuery.next();
      System.out.println("name:"
          + student.getName()
          + "\tage:"
          + student.getAge()
          + "\tsubject:"
          + student.getSubject()
          + "\ttime:"
          + new SimpleDateFormat("ss,sss").format(System
              .currentTimeMillis()));
    }      

3.3實作自己的集合

實作自己需要用到的資料存儲集合:

一般常用的庫幫助我們寫了非常多的方法,常用的List,set,Map…等等,有好多都有實作了疊代器,是以我們能夠使用這些集合的非常友善的周遊資料。

4.常見的問題

4.1在周遊的過程中删除

普通情況下,我們可能不使用疊代器進行周遊,就像這樣:

//普通for循環
for(int i = 0;i < length;i++){
//......
}
//增強for循環
for(Class c:array){
//.....
}
//Class 元素類
//c元素變量
//array數組變量      

就for循環來說,最常用可能就這兩種,其他的也有,但是比較少用,普通for在基本類型數組中較多,增強for循環在複雜類型數組及存儲集合中較多。

在周遊的過程中删除有一個比較麻煩的事情:

使用普通for循環,因為是基本數組,删除其中一個元素,意味着需要把後面的元素依次前移(有序)或者用最後一個元素替換删除元素(無序)然後删除最後一個元素,但是不管怎麼删除,因為基本類型數組,可能在一開始就限定了大小,是以,删除需要複制到新數組,需要實作複制方法。

使用增強for循環,是複雜類型數組,一般jdk實作了對這些集合的基本操作(增删取。。。。)。是以删除比較友善,可能調用個方法就行。

但是在增強for循環中直接删除會發生異常:

試驗代碼:

for(Student student:school.getList()){
      if(student.getAge()%10==4){
        school.getList().remove(student);
      }
    }      

在上面的例子中修改:

在School.java中添加一個方法:

public List<Student> getList(){
    return students;
  }      

運作結果:

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
  at java.util.AbstractList$Itr.next(AbstractList.java:343)
  at com.startime.iterator.Main.main(Main.java:33)      

檢視是AbstractList.java:372行抛出的異常:

代碼如下:

final void checkForComodification() {
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    }
    }      

其中涉及到兩個參數:modCount和expectedModCount

這兩個參數的定義如下:

/**
     * The number of times this list has been <i>structurally modified</i>.
     * Structural modifications are those that change the size of the
     * list, or otherwise perturb it in such a fashion that iterations in
     * progress may yield incorrect results.
     *
     * <p>This field is used by the iterator and list iterator implementation
     * returned by the {@code iterator} and {@code listIterator} methods.
     * If the value of this field changes unexpectedly, the iterator (or list
     * iterator) will throw a {@code ConcurrentModificationException} in
     * response to the {@code next}, {@code remove}, {@code previous},
     * {@code set} or {@code add} operations.  This provides
     * <i>fail-fast</i> behavior, rather than non-deterministic behavior in
     * the face of concurrent modification during iteration.
     *
     * <p><b>Use of this field by subclasses is optional.</b> If a subclass
     * wishes to provide fail-fast iterators (and list iterators), then it
     * merely has to increment this field in its {@code add(int, E)} and
     * {@code remove(int)} methods (and any other methods that it overrides
     * that result in structural modifications to the list).  A single call to
     * {@code add(int, E)} or {@code remove(int)} must add no more than
     * one to this field, or the iterators (and list iterators) will throw
     * bogus {@code ConcurrentModificationExceptions}.  If an implementation
     * does not wish to provide fail-fast iterators, this field may be
     * ignored.
     */
    protected transient int modCount = 0;      

看注釋猜到這個modCount應該是記錄修改次數的變量。

這是另一個參數:

/**
   * The modCount value that the iterator believes that the backing
   * List should have.  If this expectation is violated, the iterator
   * has detected concurrent modification.
   */
  int expectedModCount = modCount;      

差不多就是并發修改辨別。

總之就是這兩個值相同才是正确的,不相同表示發生錯誤。

這是ArrayList的remove方法:

public boolean remove(Object o) {
    if (o == null) {
            for (int index = 0; index < size; index++)
        if (elementData[index] == null) {
            fastRemove(index);
            return true;
        }
    } else {
        for (int index = 0; index < size; index++)
        if (o.equals(elementData[index])) {
            fastRemove(index);
            return true;
        }
        }
    return false;
    }      

發現在remove方法中并沒有對上述兩個值的改變,但是有一個方法:

private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // Let gc do its work
    }      

這個方法在remove方法中調用。

在增加的方法中:

public boolean add(E e) {
    ensureCapacity(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
    }      

add方法中調用了這個方法:

public void ensureCapacity(int minCapacity) {
    modCount++;
    int oldCapacity = elementData.length;
    if (minCapacity > oldCapacity) {
        Object oldData[] = elementData;
        int newCapacity = (oldCapacity * 3)/2 + 1;
            if (newCapacity < minCapacity)
        newCapacity = minCapacity;
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
    }
    }      

對于一個數組來說,最可能改變modCount的地方就是這兩個方法。

舉個例子,對于50個元素的arrayList的modCount的值是怎麼變化的。

首先建立一個arrayList,modCount=0(初值是0)

然後添加一個元素,modCount加1

當50個元素增加完,modCount=50,expectedmodCount=50

然後删除一個元素時,

modCount加1,modCount=51

然後對删除元素後面的元素進行向前拷貝:

拷貝的方法:

System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);      

這個方法的原型:

public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);      

看到原型方法就很清楚這個方法的參數:

源資料,源資料開始下标

目标資料,目标資料開始下标

長度。

是以,在n個元素中删除第i個

參數為:

data,i+1,data,i,n-i-1(這裡多減一是因為從0開始)

可以看到,在remove方法中删除元素,導緻modCount值+1,且對元素進行重新拷貝。

但是

expectedmodCount的值沒有發生改變。

然後我們取出下一個元素的時候,調用了擷取元素的方法:

public E next() {
            checkForComodification();
      try {
    E next = get(cursor);
    lastRet = cursor++;
    return next;
      } catch (IndexOutOfBoundsException e) {
    checkForComodification();
    throw new NoSuchElementException();
      }
  }      

這個應該是增強for循環的一種實作方式,有可能是重載了冒号這個運算符,類似Java中字元串可以用加号連接配接一樣。(我是通過加斷點的方式得出調用這個方法擷取下一個元素)

其中有一個檢測的方法:

final void checkForComodification() {
        if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    }
    }      

當modCount和expectedmodCount值不同,抛出異常。

那麼使用疊代器呢?

測試方案:去掉學生age%10==4的學生

在測試方法中進行修改,修改為以下:

import java.text.SimpleDateFormat;

public class Main {

  public static void main(String[] args) {

    Room room = new Room(10);

    RoomEach roomEach = room.iterator();
    while (roomEach.hasNext()) {
      People people = roomEach.next();
      System.out.println(people.getName() + "|||||||" + people.getAge());
    }

    School school = new School(50);
    SchoolQuery schoolQuery = (SchoolQuery) school.iterator();
    while (schoolQuery.hasNext()) {
      Student student = schoolQuery.next();
      System.out.println("name:"
          + student.getName()
          + "\tage:"
          + student.getAge()
          + "\tsubject:"
          + student.getSubject()
          + "\ttime:"
          + new SimpleDateFormat("ss,sss").format(System
              .currentTimeMillis()));
      if(student.getAge()%10 == 4){
        schoolQuery.remove();
      }
    }

    schoolQuery = (SchoolQuery) school.iterator();
    while (schoolQuery.hasNext()) {
      Student student = schoolQuery.next();
      System.out.println("name:"
          + student.getName()
          + "\tage:"
          + student.getAge()
          + "\tsubject:"
          + student.getSubject()
          + "\ttime:"
          + new SimpleDateFormat("ss,sss").format(System
              .currentTimeMillis()));
    }

    
//    for(Student student:school.getList()){
//      if(student.getAge()%10==4){
//        school.getList().remove(student);
//      }
//    }
    
    
  }

}
      

執行結果:

03:15:19|||||||827
03:15:19|||||||827
03:15:19|||||||827
03:15:19|||||||827
03:15:19|||||||827
03:15:19|||||||827
03:15:19|||||||827
03:15:19|||||||827
03:15:19|||||||828
03:15:19|||||||828
name:0  age:0 subject:0 time:19,019
name:1  age:1 subject:1 time:19,019
name:2  age:2 subject:2 time:19,019
name:3  age:3 subject:3 time:19,019
name:4  age:4 subject:4 time:19,019
name:6  age:6 subject:6 time:19,019
name:7  age:7 subject:7 time:19,019
name:8  age:8 subject:8 time:19,019
name:9  age:9 subject:9 time:19,019
name:10 age:10  subject:10  time:19,019
name:11 age:11  subject:11  time:19,019
name:12 age:12  subject:12  time:19,019
name:13 age:13  subject:13  time:19,019
name:14 age:14  subject:14  time:19,019
name:16 age:16  subject:16  time:19,019
name:17 age:17  subject:17  time:19,019
name:18 age:18  subject:18  time:19,019
name:19 age:19  subject:19  time:19,019
name:20 age:20  subject:20  time:19,019
name:21 age:21  subject:21  time:19,019
name:22 age:22  subject:22  time:19,019
name:23 age:23  subject:23  time:19,019
name:24 age:24  subject:24  time:19,019
name:26 age:26  subject:26  time:19,019
name:27 age:27  subject:27  time:19,019
name:28 age:28  subject:28  time:19,019
name:29 age:29  subject:29  time:19,019
name:30 age:30  subject:30  time:19,019
name:31 age:31  subject:31  time:19,019
name:32 age:32  subject:32  time:19,019
name:33 age:33  subject:33  time:19,019
name:34 age:34  subject:34  time:19,019
name:36 age:36  subject:36  time:19,019
name:37 age:37  subject:37  time:19,019
name:38 age:38  subject:38  time:19,019
name:39 age:39  subject:39  time:19,019
name:40 age:40  subject:40  time:19,019
name:41 age:41  subject:41  time:19,019
name:42 age:42  subject:42  time:19,019
name:43 age:43  subject:43  time:19,019
name:44 age:44  subject:44  time:19,019
name:46 age:46  subject:46  time:19,019
name:47 age:47  subject:47  time:19,019
name:48 age:48  subject:48  time:19,019
name:49 age:49  subject:49  time:19,019
name:0  age:0 subject:0 time:19,019
name:1  age:1 subject:1 time:19,019
name:2  age:2 subject:2 time:19,019
name:3  age:3 subject:3 time:19,019
name:5  age:5 subject:5 time:19,019
name:6  age:6 subject:6 time:19,019
name:7  age:7 subject:7 time:19,019
name:8  age:8 subject:8 time:19,019
name:9  age:9 subject:9 time:19,019
name:10 age:10  subject:10  time:19,019
name:11 age:11  subject:11  time:19,019
name:12 age:12  subject:12  time:19,019
name:13 age:13  subject:13  time:19,019
name:15 age:15  subject:15  time:19,019
name:16 age:16  subject:16  time:19,019
name:17 age:17  subject:17  time:19,019
name:18 age:18  subject:18  time:19,019
name:19 age:19  subject:19  time:19,019
name:20 age:20  subject:20  time:19,019
name:21 age:21  subject:21  time:19,019
name:22 age:22  subject:22  time:19,019
name:23 age:23  subject:23  time:19,019
name:25 age:25  subject:25  time:19,019
name:26 age:26  subject:26  time:19,019
name:27 age:27  subject:27  time:19,019
name:28 age:28  subject:28  time:19,019
name:29 age:29  subject:29  time:19,019
name:30 age:30  subject:30  time:19,019
name:31 age:31  subject:31  time:19,019
name:32 age:32  subject:32  time:19,019
name:33 age:33  subject:33  time:19,019
name:35 age:35  subject:35  time:19,019
name:36 age:36  subject:36  time:19,019
name:37 age:37  subject:37  time:19,019
name:38 age:38  subject:38  time:19,019
name:39 age:39  subject:39  time:19,019
name:40 age:40  subject:40  time:19,019
name:41 age:41  subject:41  time:19,019
name:42 age:42  subject:42  time:19,019
name:43 age:43  subject:43  time:19,019
name:45 age:45  subject:45  time:19,019
name:46 age:46  subject:46  time:19,019
name:47 age:47  subject:47  time:19,019
name:48 age:48  subject:48  time:19,019
name:49 age:49  subject:49  time:19,019
      

那為什麼使用疊代器就是安全的?

首先在調試的時候有一個檔案AbstractList$Itr這個檔案:

public void remove() {
      if (lastRet == -1)
    throw new IllegalStateException();
            checkForComodification();

      try {
    AbstractList.this.remove(lastRet);
    if (lastRet < cursor)
        cursor--;
    lastRet = -1;
    expectedModCount = modCount;
      } catch (IndexOutOfBoundsException e) {
    throw new ConcurrentModificationException();
      }
  }      

使用疊代器删除,我看其他講這個的文章說的大概意思就是疊代器建立了一個副本(這個可以了解,我們實作疊代器的時候有一個參數:private School school;)是以呢,在副本中進行操作是因為我們在疊代器中實作了方法。而且使用疊代器的删除使用的是這個remove方法,在方法中對expectedmodCount值進行更新,是以不會報錯。(還有網上解釋說有其他的變量,我暫時還沒有搞懂,是以這隻是我的一個猜測)

4.2周遊的并發安全性

繼續閱讀