詳解JAVA中的for-each循環(huán)與迭代
在學(xué)習(xí)java中的collection時(shí)注意到,collection層次的根接口Collection實(shí)現(xiàn)了Iterable<T>接口(位于java.lang包中),實(shí)現(xiàn)這個(gè)接口允許對(duì)象成為 "foreach" 語(yǔ)句的目標(biāo),而此接口中的唯一方法,實(shí)現(xiàn)的就是返回一個(gè)在一組 T 類型的元素上進(jìn)行迭代的迭代器。
一、迭代器Iterator
接口:Iterator<T>
public interface Iterator<E>{
boolean hasNext();
E next();
void remove();
}
查看Iterator接口API可以知道,這是對(duì)collection進(jìn)行迭代的迭代器。迭代器允許調(diào)用者利用定義良好的語(yǔ)義在迭代期間從迭代器所指向的 collection 移除元素。
尤其值得注意的是此迭代器remove()方法的使用:從迭代器指向的 collection 中移除迭代器返回的最后一個(gè)元素(可選操作)。每次調(diào)用 next 只能調(diào)用一次此方法。如果進(jìn)行迭代時(shí)用調(diào)用此方法(remove方法)之外的其他方式修改了該迭代器所指向的 collection,則迭代器的行為是不確定的。 接口設(shè)計(jì)人員在設(shè)計(jì)Iterator<T>接口的時(shí)候已經(jīng)指出,在進(jìn)行迭代時(shí)如果調(diào)用了除了迭代器的remove()方法修改了該迭代器所指向的collection,則會(huì)造成不確定的后果。具體出現(xiàn)什么后果依迭代器的具體實(shí)現(xiàn)而定。針對(duì)這種不確定的后果可能出現(xiàn)的情況,在學(xué)習(xí)ArrayList時(shí)遇到了其中一種:迭代器拋出 ConcurrentModificationException異常。具體異常情況如下代碼所示:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class ItaratorTest {
public static void main(String[] args) {
Collection<String> list = new ArrayList<String>();
list.add("Android");
list.add("IOS");
list.add("Windows Mobile");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String lang = iterator.next();
list.remove(lang);//will throw ConcurrentModificationException
}
}
}
此段代碼在運(yùn)行時(shí)會(huì)拋出ConcurrentModificationException異常,因?yàn)槲覀冊(cè)诘鬟\(yùn)行期間沒(méi)有用iterator的remove()方法來(lái)刪除元素,而是使用ArrayList的 remove()方法改變了迭代器所指向的collection。這就違反了迭代器的設(shè)計(jì)原則,所以發(fā)生了異常。
所報(bào)異常情況如下所示:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at Text.ItaratorTest.main(ItaratorTest.java:17)
二、for-each循環(huán)與迭代器Iterator<T>
從Java5起,在Java中有了for-each循環(huán),可以用來(lái)循環(huán)遍歷collection和array。Foreach循環(huán)允許你在無(wú)需保持傳統(tǒng)for循環(huán)中的索引,或在使用iterator /ListIterator(ArrayList中的一種迭代器實(shí)現(xiàn))時(shí)無(wú)需調(diào)用while循環(huán)中的hasNext()方法就能遍歷collection。for-each循環(huán)簡(jiǎn)化了任何Collection或array的遍歷過(guò)程。但是使用foreach循環(huán)也有兩點(diǎn)需要注意。
使用foreach循環(huán)的對(duì)象,必須實(shí)現(xiàn)了Iterable<T>接口
請(qǐng)看如下示例:
import java.util.ArrayList;
public class ForeachTest1 {
public static void main(String args[]) {
CustomCollection<String> myCollection = new CustomCollection<String>();
myCollection.add("Java");
myCollection.add("Scala");
myCollection.add("Groovy");
// What does this code will do, print language, throw exception or
// compile time error
for (String language : myCollection) {
System.out.println(language);
}
}
private class CustomCollection<T> {
private ArrayList<T> bucket;
public CustomCollection() {
bucket = new ArrayList();
}
public int size() {
return bucket.size();
}
public boolean isEmpty() {
return bucket.isEmpty();
}
public boolean contains(T o) {
return bucket.contains(o);
}
public boolean add(T e) {
return bucket.add(e);
}
public boolean remove(T o) {
return bucket.remove(o);
}
}
}
上述代碼將無(wú)法通過(guò)編譯,這是因?yàn)榇a中的CustomCollection類沒(méi)有實(shí)現(xiàn)Iterable<T>接口,編譯期的報(bào)錯(cuò)如下:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Can only iterate over an array or an instance of java.lang.Iterable
at Text.ForeachTest1.main(ForeachTest1.java:15)
事實(shí)上,無(wú)需等到編譯時(shí)才發(fā)現(xiàn)報(bào)錯(cuò),eclipse會(huì)在這段代碼寫完之后就會(huì)在foreach循環(huán)處顯示錯(cuò)誤:Can only iterate over an array or an instance of java.lang.Iterable
從上述示例可以再次得到確認(rèn)的是,foreach循環(huán)只適用于實(shí)現(xiàn)了Iterable<T>接口的對(duì)象。由于所有內(nèi)置Collection類都實(shí)現(xiàn)了java.util.Collection接口,已經(jīng)繼承了Iterable,所以為了解決上述問(wèn)題,可以選擇簡(jiǎn)單地讓CustomCollection實(shí)現(xiàn)Collection接口或者繼承AbstractCollection。解決方式如下:
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Iterator;
public class ForeachTest {
public static void main(String args[]) {
CustomCollection<String> myCollection = new CustomCollection<String>();
myCollection.add("Java");
myCollection.add("Scala");
myCollection.add("Groovy");
for (String language : myCollection) {
System.out.println(language);
}
}
private static class CustomCollection<T> extends AbstractCollection<T> {
private ArrayList<T> bucket;
public CustomCollection() {
bucket = new ArrayList();
}
public int size() {
return bucket.size();
}
public boolean isEmpty() {
return bucket.isEmpty();
}
public boolean contains(Object o) {
return bucket.contains(o);
}
public boolean add(T e) {
return bucket.add(e);
}
public boolean remove(Object o) {
return bucket.remove(o);
}
@Override
public Iterator<T> iterator() {
// TODO Auto-generated method stub
return bucket.iterator();
}
}
}
2.foreach循環(huán)的內(nèi)部實(shí)現(xiàn)也是依靠Iterator進(jìn)行實(shí)現(xiàn)的
為了驗(yàn)證foreach循環(huán)是使用Iterator作為內(nèi)部實(shí)現(xiàn)這一事實(shí),我們依然采用本文最開(kāi)始的實(shí)例進(jìn)行驗(yàn)證:
public class ItaratorTest {
public static void main(String[] args) {
Collection<String> list = new ArrayList<String>();
list.add("Android");
list.add("IOS");
list.add("Windows Mobile");
// example1
// Iterator<String> iterator = list.iterator();
// while (iterator.hasNext()) {
// String lang = iterator.next();
// list.remove(lang);
// }
// example 2
for (String language : list) {
list.remove(language);
}
}
}
程序運(yùn)行時(shí)所報(bào)異常:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at Text.ItaratorTest.main(ItaratorTest.java:22)
此異常正說(shuō)明了for-each循環(huán)內(nèi)部使用了Iterator來(lái)遍歷Collection,它也調(diào)用了Iterator.next(),這會(huì)檢查(元素的)變化并拋出ConcurrentModificationException。
總結(jié):
- 在遍歷collection時(shí),如果要在遍歷期間修改collection,則必須通過(guò)Iterator/listIterator來(lái)實(shí)現(xiàn),否則可能會(huì)發(fā)生“不確定的后果”。
- foreach循環(huán)通過(guò)iterator實(shí)現(xiàn),使用foreach循環(huán)的對(duì)象必須實(shí)現(xiàn)Iterable接口
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Java中遍歷數(shù)組使用foreach循環(huán)還是for循環(huán)?
- java程序中foreach用法示例
- 淺析java的foreach循環(huán)
- 深入了解java8的foreach循環(huán)
- 深入理解java中for和foreach循環(huán)
- java 使用foreach遍歷集合元素的實(shí)例
- js的for in循環(huán)和java里foreach循環(huán)的區(qū)別分析
- Java for-each循環(huán)使用難題2例(高級(jí)使用方法)
- 淺談java 增強(qiáng)型的for循環(huán) for each
- Java徹底消滅if-else的8種方案
相關(guān)文章
Java中一維二維數(shù)組的靜態(tài)和動(dòng)態(tài)初始化
今天通過(guò)本文給大家分享Java中的數(shù)組,包括一維數(shù)組和二維數(shù)組的靜態(tài)初始化和動(dòng)態(tài)初始化問(wèn)題,感興趣的朋友一起看看吧2017-10-10
Maven本地打包war包實(shí)現(xiàn)代碼解析
這篇文章主要介紹了Maven本地打包war包實(shí)現(xiàn)代碼解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
Java并發(fā)編程之volatile與JMM多線程內(nèi)存模型
這篇文章主要介紹了Java并發(fā)volatile與JMM多線程內(nèi)存模型,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05
解決使用@Value(${×××))從properties文件取值的坑
這篇文章主要介紹了解決使用@Value(${×××))從properties文件取值的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Java打印星號(hào)圖案和數(shù)字圖案的示例代碼
在 Java 中打印圖案是一項(xiàng)常見(jiàn)的編程任務(wù),尤其在初學(xué)階段,通過(guò)以特定方式排列符號(hào)或數(shù)字,可以形成各種設(shè)計(jì)或形狀,這些圖案不僅有助于解決問(wèn)題,還能培養(yǎng)算法思維能力,本文將討論如何在 Java 中打印圖案,并探索一些最常見(jiàn)的圖案類型,需要的朋友可以參考下2024-11-11
maven項(xiàng)目在實(shí)踐中的構(gòu)建管理之路的方法
這篇文章主要介紹了maven項(xiàng)目在實(shí)踐中的構(gòu)建管理之路的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-05-05
SpringBoot集成Hadoop對(duì)HDFS的文件操作方法
這篇文章主要介紹了SpringBoot集成Hadoop對(duì)HDFS的文件操作方法,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-07-07

