文章目录
- 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种设计模式