關于for丶foreach丶iterator 迭代器
1丶前言:我們在網上或者在討論的時候往往會有這么一種說法:foreach 也是迭代器的一類,底層實作的是迭代器,但是這種說法并不嚴謹,也可以說這種說法對于我們正常的理解來說并不是能夠完全理解,
2丶首先我來說出結論:for 丶 foreach 丶迭代器 這三個遍歷方法中 不應該將他們三種遍歷方法歸在一起 而是將其分成三類)——不要將foreach 歸為迭代器的一種遍歷
(我們一步一步來,首先來簡單介紹一下這是那種方法的區別)
3丶區別:
1)形式區別:
對于for回圈,我們采用:
for(int i=0;i<arr.size();i++){…}
對于foreach:
for(int i:arr){…}
(在這里用int 型別舉個例子)
對于迭代器的形式:
Iterator it = arr.iterator();
while(it.hasNext()){ object o =it.next(); …}
;
2)條件差別
for需要知道陣列或者集合的大小,而且需要時有序的,不然無法遍歷;
foreach和iterator不需要知道陣列或者集合的大小,他們都是得到集合內的每一個元素然后進行處理;
3)多型差別
for和foreach都需要知道自己的集合型別,甚至要知道自己集合內的元素型別,不能實作多型,(
public static void main(String[] args) {
int[] ints = new int[]{1, 2, 3, 4};
int[] var2 = ints;
int var3 = ints.length;
for(int var4 = 0; var4 < var3; ++var4) {
int i = var2[var4];
System.out.println(i);
}
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(789);
coll.add(new Person(1001, “Tom”));
Iterator var7 = coll.iterator();
while(var7.hasNext()) {
Object object = var7.next();
System.out.println(object);
}
}
這差不多是增強for的底層原始碼 我們可以清楚的看到 當我們遍歷陣列的時候他會用for回圈倆遍歷,放我們遍歷結合的時候他會用迭代器來使用(你可能感到這與上面我所得出的結論相違背,下面的講解我會說明)
)
Iterator是一個介面型別,它不關心集合的累心和集合內的元素型別,因為它是通過hasnext和next來進行下一個元素的判斷和獲取,這一切都是在集合型別定義的時候就完成的事情,迭代器統一了對容器的訪問模式,這也是對介面解耦的最好表現,(lIterator物件稱為迭代器(設計模式的一種),主要用于遍歷 Collection 集合中的元素,
GOF給迭代器模式的定義為:提供一種方法訪問一個容器(container)物件中各個元素,而又不需暴露該物件的內部細節,迭代器模式,就是為容器而生,)
4)用法差別
for一般可以用于簡單的順序集合,并且可以預測集合的大小(一定要有序,因為i這個索引,所以遍歷是一定要有序);
foreach可以遍歷任何集合或者陣列,但是使用者需要知道遍歷元素的型別
iterator是最強大的,它可以隨之修改元素內部的元素,可以在遍歷的時刻用remove()
而且iterator不需要知道元素型別和元素大小,通過hasnext()判斷是否遍歷完所有元素,
而且在對范型的集合進行遍歷的時候,iterator是唯一的選擇,就是因為不需要知道元素型別便可以遍歷,
4丶代碼分析:
我們來看一下代碼:
import java.util.*;
public class TreeSetTest {
public static void main(String[] args) {
List list=new ArrayList();
list.add("1");
list.add("2");
/*for(String temp : list){
if("1".equals(temp))
list.remove(temp);
}
for(String temp : list){
System.out.println(temp);
}*/
Iterator<String> list1= list.iterator();
while (list1.hasNext()){
String next = list1.next();
if ("2".equals(next)){
list1.remove();
}
}
list1=list.iterator();
while (list1.hasNext()){
System.out.println(list1.next());
}
}
這樣可以將“2”洗掉
import java.util.*;
public class TreeSetTest {
public static void main(String[] args) {
List list=new ArrayList();
list.add("1");
list.add("2");
for(String temp : list){
if("2".equals(temp))
list.remove(temp);
}
for(String temp : list){
System.out.println(temp);
}
/*Iterator<String> list1= list.iterator();
while (list1.hasNext()){
String next = list1.next();
if ("2".equals(next)){
list1.remove();
}
}
list1=list.iterator();
while (list1.hasNext()){
System.out.println(list1.next());
}*/
}
我們運行這寫代碼會拋出例外
Exception in thread “main” java.util.ConcurrentModificationException
at java.base/java.util.ArrayList
I
t
r
.
c
h
e
c
k
F
o
r
C
o
m
o
d
i
f
i
c
a
t
i
o
n
(
A
r
r
a
y
L
i
s
t
.
j
a
v
a
:
1009
)
a
t
j
a
v
a
.
b
a
s
e
/
j
a
v
a
.
u
t
i
l
.
A
r
r
a
y
L
i
s
t
Itr.checkForComodification(ArrayList.java:1009) at java.base/java.util.ArrayList
Itr.checkForComodification(ArrayList.java:1009)atjava.base/java.util.ArrayListItr.next(ArrayList.java:963)
at 集合復習.TreeSetTest.main(TreeSetTest.java:13)
ConcurrentModificationException的原始碼:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
這里設計一個java關于多執行緒的知識,因為iterator是禁止多執行緒操作的,java用了一個機制,就是modcount,記錄對集合進行改變的數量,創建一個迭代器與當前集合是要緊耦合的,我不能再我遍歷的時候同時去改變這個集合元素,不然會造成混亂,
(介面解藕:不管物件實作的程序,只關心他的結果,
比如我每天要去上課,每天都要定鬧鈴,但是我可以用鬧鈴實作,如果鬧鈴壞了,我可以用手機去繼承鬧鈴的功能,用手機來實作鬧鈴的功能,
再比如,女人愛上了男人,男人也愛上女人,但我不管這個男人或者女人是誰只要是個男或者女人就行,
耦合理解相對比較容易,就是兩個或兩個以上的體系或兩種運動形式間通過相互作用而彼此影響以至聯合起來的現象,)
在這里我們已經知道是modcount不等于expectedmodecount造成的錯誤
我們可一看到報錯的來源 at java.base/java.util.ArrayList$Itr.next(ArrayList.java:963)
用idea打開之后查看原始碼是Itr(Iterator介面的實作類)這個私有類里面的next是foreach擅自為我們進行構造的迭代器,而我們沒有使用這個迭代器里面的remove()
但是我們在這時看自己的代碼 是沒有創建list的迭代器的,也就是說我們使用的是Arraylist本身自己的remove()方法,
我們再看一下各自的remove()的原始碼:
1丶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;
}
/*
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
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; // clear to let GC do its work
}
Arraylist自身方法的remove是不會去調整exceptedmodcount的值,但是卻增加了modcount,所以在foreach陳述句中使用next的checkmodcount的時候發生了錯誤,
2丶Itr私有迭代器的remove():
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount; ——>讓 modCount 賦值給 expectedModCount 使其不報錯
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
我們可以看到迭代器中的remove有意識的改變expectedmodcount來使其不報錯,而foreach使用的Arraylist.remove,改變了modcount,所以才會出現報錯現象,
可是你在這時會想到為什么洗掉“1”的時候他不報錯?
那是是因為洗掉過后foreach會呼叫迭代器中的hasnext()進行判斷,
代碼如下:
public boolean hasNext() {
return cursor != size;
}
這個時候沒有下一個元素,所以不會Arraylist.remove()后呼叫Itr.next()方法,所以不會進行判斷,
Itr.next()的代碼:
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
可以看到,在Itr.next()的一開始便檢查modcount于expectedmodcount是否相同,
通過上面的了解,我們使用iterator為什么能夠在遍歷的時候進行洗掉操作也可以理解了:
在Itr.remove()方法內有意的將expectedmodcount賦值為modcount,所以不會拋出例外,
最后還是強調一句話:不要將foreach 歸為迭代器的一種遍歷,兩者之間是有一定的區別的,
(本人第一次寫博客,完全在自己的能力范圍之內,如有不好的地方,還請多多包涵),
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/222830.html
標籤:java
上一篇:Tomcat原始碼學習記錄
