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

后续添加。

(有问题欢迎评论)
23种设计模式

    原文作者:算法小白
    原文地址: https://blog.csdn.net/a18792721831/article/details/82260476
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞