文章目錄
- 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值進行更新,是以不會報錯。(還有網上解釋說有其他的變量,我暫時還沒有搞懂,是以這隻是我的一個猜測)