文章目录


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

后续添加。

(有问题欢迎评论)