23种设计模式----迭代器模式----行为模式

版权声明:本文为博主原创文章,转载请注明作者。 https://blog.csdn.net/a18792721831/article/details/82260476

迭代器模式

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遍历的并发安全性

后续添加。

(有问题欢迎评论)

猜你喜欢

转载自blog.csdn.net/a18792721831/article/details/82260476