用 for/in 在 Java 5.0 中增強循環(huán)
for/in 循環(huán)通常叫作增強的 for 或者 foreach,它是 Java 5.0 中一個(gè)極為方便的特性。實(shí)際上它沒(méi)有提供任何新的功能,但它顯然能讓一些日常編碼任務(wù)變得更簡(jiǎn)單一些。在本文中,您將學(xué)習這方面的許多內容,其中包括使用 for/in 在數組和集合中進(jìn)行遍歷,以及如何用它避免不必要(或者只是令人厭煩的)類(lèi)型轉換。您還將學(xué)習如何實(shí)現 for/in,了解新的 Iterable 接口的一些細節,甚至還將學(xué)習如何讓您自己的定制對象用這個(gè)新的構造進(jìn)行遍歷。最后,您將學(xué)習 for/in 不能 做什么,以確保您能理解什么時(shí)候選擇原來(lái)的 for 是正確的選擇。
越短越好
這是資深電腦程序員都知道的一條最基本的原理:因為更短 意味著(zhù)打字更少,所以更短自然也就更好。這個(gè)哲學(xué)造就了 vi 這樣的 IDE,在這類(lèi) IDE 中,像 :wq! 和 28G 這樣的命令擁有豐富的含義。這個(gè)哲學(xué)還導致一些最神秘的代碼,比如說(shuō),變量 ar 代表 Agile Runner(也可能是 Argyle,或者 Atomic Reactor 等等,總之,您明白就好)。
有些時(shí)候,在努力實(shí)現短小的時(shí)候,程序員會(huì )將明確性?huà)伒侥X后。也就是說(shuō),過(guò)于短小和過(guò)于繁冗的代碼都會(huì )讓人感到痛苦不堪。變量名為 theAtomicReactorLocatedInPhiladelphia 與名為 ar 的變量一樣讓人討厭和不方便。一定會(huì )有一個(gè)讓人高興的解決方法,不是嗎?
這個(gè)讓人高興的方法(至少我是這么認為的)是以尋找完成某事的方便 途徑為出發(fā)點(diǎn),不是為了短小而短小。作為這類(lèi)解決方案的一個(gè)好例子,Java 5.0 引入了新版的 for 循環(huán),我把它稱(chēng)為 for/in。它也被稱(chēng)為 foreach,有時(shí)也叫作增強的 for,但這些指的都是同一個(gè)構造。不管您叫它什么,for/in 都會(huì )使代碼變得更簡(jiǎn)單,正如您在本文中將看到的那樣。
不使用 Iterator
使用 for/in 與“普通”for 之間的最基本區別是,您不必使用計數器(通常稱(chēng)為 i 或 count)或 Iterator。參見(jiàn)清單 1,它顯示了一個(gè)使用的 Iterator 的for 循環(huán):
清單 1. for 循環(huán),舊式學(xué)院風(fēng)格
public void testForLoop(PrintStream out) throws IOException {
List list = getList(); // initialize this list elsewhere
for (Iterator i = list.iterator(); i.hasNext(); ) {
Object listElement = i.next();
out.println(listElement.toString());
// Do something else with this list element
}
}
注意:如果您一直在看我寫(xiě)的關(guān)于 Tiger 新特性的文章(請參閱參考資料),您就會(huì )知道,我常常感謝 O\'Reilly Media, Inc.,因為它們允許我在本文中發(fā)布我其他書(shū)中的代碼示例。這意味著(zhù)您得到的代碼已經(jīng)通過(guò)了更多測試、更多評論,比我能提供給您的多得多。所以再次感謝 O\'Reilly,如果您想了解 Tiger 的更多內容,請參考我撰寫(xiě)的一些書(shū),它們列在參考資源一節中,其中有完整的鏈接和更多的細節。
如果您期待著(zhù)得到如何把這個(gè)代碼轉變成新的 for/in 循環(huán)的詳細解釋?zhuān)铱峙乱屇?。清?2 顯示了用 for/in 改寫(xiě)的清單 1 中的代碼,您應該相當熟悉它。請參見(jiàn)下面代碼清單,我將盡可能詳細地解釋 for/in 循環(huán)(但是仍然很難湊成一章)。
清單 2. 轉換成 for/in
public void testForInLoop(PrintStream out) throws IOException {
List list = getList(); // initialize this list elsewhere
for (Object listElement : list) {
out.println(listElement.toString());
// Do something else with this list element
}
}
for/in 循環(huán)的基本語(yǔ)法如清單 3 所示。如果您還不習慣閱讀規范,那么該語(yǔ)法可能看起來(lái)有點(diǎn)古怪,但是當您一個(gè)部分一個(gè)部分了解它的時(shí)候,您會(huì )發(fā)現閱讀它實(shí)際上非常容易。
清單 3. for/in 循環(huán)的基本結構
for(聲明:表達式)
語(yǔ)句
for/in 因何得名
細心的讀者會(huì )注意到,所謂 for/in 根據不包含單詞 in。它的名字來(lái)自借閱的閱讀方式。在清單 2 中,您會(huì )說(shuō) for 每個(gè)對象 in 命名變量列表中,執行 ...。當然,省略號代表循環(huán)實(shí)質(zhì)做的操作。您如何看待會(huì )有些差異,但是在每種表達方式中 for 和 in 都是突出的。
聲明 是一個(gè)變量,例如 Object listElement。這個(gè)變量應該有自己的類(lèi)型,這樣,它就可以與將遍歷的列表、數組或集合中的每一個(gè)項兼容。在清單 2 的例子中,list 包含一些對象,因此這些對象就是 listElement 的類(lèi)型。
表達式 就是一個(gè)表達式。它計算的結果應當是可以遍歷的(后面再詳加介紹)。在現在,只要保證表達式 計算的結果是一個(gè)集合或者數組就可以了。表達式可以簡(jiǎn)單到就是一個(gè)變量(如清單 2 所示)或者是一個(gè)方法調用(例如 getList()),亦或是包含布爾邏輯或三目運算符的復雜表達式。只要它返回一個(gè)數組或集合,就一切 OK。
語(yǔ)句 代表循環(huán)的內容,它對聲明 中定義的變量進(jìn)行操作;當然,這是一個(gè)循環(huán),所以語(yǔ)句 將應用到數組中集合的每個(gè)項目上。而且,使用大括號({ 和 })時(shí),還能使用多條語(yǔ)句。
其用法如下:創(chuàng )建一個(gè)變量,指向要遍歷的數組或集合,然后對定義的變量進(jìn)行操作。不用對列表中的每個(gè)項目進(jìn)行賦值,因為 for/in 替您處理了這件事。當然,如果您還覺(jué)得不太清楚,沒(méi)關(guān)系,繼續讀下去,有大量的示例讓您足夠清楚這個(gè)事件。
但是,在進(jìn)行下一步之前,我想用更加符合規范的方式說(shuō)明 for/in 的工作方式。清單 4 顯示了在提供通用化類(lèi)型時(shí),實(shí)際發(fā)揮作用的 for/in 循環(huán)。以下是編譯器把該循環(huán)轉換成普通的 for 循環(huán)之后,語(yǔ)句實(shí)際看起來(lái)的樣子。
您明白了嗎?編譯器實(shí)際上把這個(gè)更短、更方便的 for/in 語(yǔ)句變成了一個(gè)更加編譯器友好的 for 循環(huán),而且您不會(huì )受到這項工作的影響。這就是為什么我認為它方便,而不僅僅說(shuō)它更簡(jiǎn)短的原因。
清單 4. 轉換后的 for/in 循環(huán),帶有一個(gè) Iterable
for (Iterator<E> #i = (expression).iterator(); #i.hasNext(); ) {
declaration = #i.next();
statement
}
清單 5 是另外一個(gè)經(jīng)過(guò)編譯器轉換之后的 for/in,這次沒(méi)有通用化類(lèi)型。雖然更簡(jiǎn)單,但做的事是一樣的。但是在每種情況下,您都可以很容易地在腦子里(并通過(guò)編程方式)把 for/in 語(yǔ)句轉換成普通的 for 語(yǔ)句,如果您能在腦子子里做這個(gè)轉換,事情就變得極為容易了。
清單 5. 轉換后的 for/in 循環(huán),沒(méi)有未經(jīng)參數化的類(lèi)型
for (Iterator #i = (expression).iterator(); #i.hasNext(); ) {
declaration = #i.next();
statement
}
使用數組
現在您已經(jīng)了解了基本的語(yǔ)義,可以繼續了解一些更具體的示例了。您已經(jīng)看到 for/in 如何處理列表了;處理數組也一樣容易。與集合相同,數組也被賦值(如清單 6 所示),然后這些值被逐個(gè)取出,并被處理。
清單 6. 簡(jiǎn)單的數組初始化
int[] int_array = new int[4];
String[] args = new String[10];
float[] float_array = new float[20];
對于使用 for 以及計算器或索引變量的場(chǎng)合,現在就可以使用 for/in(當然,前提是您正在使用 Tiger)。清單 7 顯示了另外一個(gè)簡(jiǎn)單的示例:
清單 7. 用 for/in 對數組進(jìn)行循環(huán)就是小菜一碟
public void testArrayLooping(PrintStream out) throws IOException {
int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };
// Print the primes out using a for/in loop
for (int n : primes) {
out.println(n);
}
}
沒(méi)有任何需要特別說(shuō)明的地方,這些都是非?;镜臇|西。數組被類(lèi)型化,所以您需要很清楚地知道數組中每個(gè)項目的變量類(lèi)型是什么。這個(gè)示例創(chuàng )建了變量(在這個(gè)示例中名為 n),然后對這個(gè)變量進(jìn)行操作。非常簡(jiǎn)單,不是嗎?我告訴過(guò)您在這里沒(méi)有什么復雜的東西。
實(shí)際上,數據中有什么類(lèi)型并不是問(wèn)題,您只需為聲明 選擇好正確的類(lèi)型就可以了。在清單 8 中,數組的元素是 Lists。所以您得到的實(shí)際上是一個(gè)集合數組。同樣,使用 for/in 就能使這些變得非常簡(jiǎn)單。
清單 8. 用 for/in 還可以在對象數組上循環(huán)
public void testObjectArrayLooping(PrintStream out) throws IOException {
List[] list_array = new List[3];
list_array[0] = getList();
list_array[1] = getList();
list_array[2] = getList();
for (List l : list_array) {
out.println(l.getClass().getName());
}
}
甚至還可以在 for/in 循環(huán)中再加上一層循環(huán),如清單 9 所示:
清單 9. 在 for/in 內部使用 for/in 不會(huì )有任何問(wèn)題!
public void testObjectArrayLooping(PrintStream out) throws IOException {
List[] list_array = new List[3];
list_array[0] = getList();
list_array[1] = getList();
list_array[2] = getList();
for (List l : list_array) {
for (Object o : l) {
out.println(o);
}
}
}
處理集合
同樣,簡(jiǎn)單性也是我們關(guān)注的內容。使用 for/in 對集合進(jìn)行遍歷沒(méi)有任何需要特殊處理或者復雜的地方,它工作起來(lái),與您剛才看到的處理列表和集合的方式一樣。清單 10 演示了一個(gè)在 List 和 Set 上遍歷的示例,毫無(wú)驚人之處。與往常一樣,我們將研究代碼,確保您了解發(fā)生的事情。
清單 10. 以下程序中有許多簡(jiǎn)單循環(huán),演示了如何使用 for/in
package com.oreilly.tiger.ch07;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ForInDemo {
public static void main(String[] args) {
// These are collections to iterate over below
List wordlist = new ArrayList();
Set wordset = new HashSet();
// Basic loop, iterating over the elements of an array
// The body of the loop is executed once for each element of args[].
// Each time through, one element is assigned to the variable word.
System.out.println(\"Assigning arguments to lists...\");
for (String word : args) {
System.out.print(word + \" \");
wordlist.add(word);
wordset.add(word);
}
System.out.println();
// Iterate through the elements of the List now
// Since lists have an order, these words should appear as above
System.out.println(\"Printing words from wordlist \" +
\"(ordered, with duplicates)...\");
for (Object word : wordlist) {
System.out.print((String)word + \" \");
}
System.out.println();
// Do the same for the Set. The loop looks the same but by virtue
// of using a Set, word order is lost, and duplicates are discarded.
System.out.println(\"Printing words from wordset \" +
\"(unordered, no duplicates)...\");
for (Object word : wordset) {
System.out.print((String)word + \" \");
}
}
}
清單 11 顯示了這個(gè)程序的輸出(在命令行上輸出了一些用來(lái)演示的數據):
清單 11. 輸出正是您想要的 —— 許多打??!
run-ch07:
[echo] Running Chapter 7 examples from Java 5.0 Tiger: A Developer\'s Notebook
[echo] Running ForInDemo...
[java] Assigning arguments to lists...
[java] word1 word2 word3 word4 word1
[java] Printing words from wordList (ordered, with duplicates)...
[java] word1 word2 word3 word4 word1
[java] Printing words from wordset (unordered, no duplicates)...
[java] word4 word1 word3 word2
類(lèi)型轉換之痛
迄今為止,在處理集合的時(shí)候,您已經(jīng)看到 for/in 使用通用的變量類(lèi)型,例如 Object。這么做很好,但是沒(méi)有真正利用到 Tiger 的另一項特性 —— 泛型(有時(shí)也叫作參數化類(lèi)型)。我把泛型的細節留給 developerWorks 即將針對這個(gè)主題推出的教程,但是泛型讓 for/in 變得更加強大。
記得 for/in 語(yǔ)句的聲明 部分創(chuàng )建了一個(gè)變量,它代表要遍歷的集合中每個(gè)項目的類(lèi)型。在數組中,類(lèi)型非常明確,因為類(lèi)型是強類(lèi)型的,int[] 只能包含整數,所以在循環(huán)中沒(méi)有類(lèi)型轉換。在您通過(guò)泛型使用類(lèi)型化列表時(shí),也有可能做到這點(diǎn)。清單 12 演示了幾個(gè)簡(jiǎn)單的參數化集合:
清單 12. 向集合類(lèi)型添加參數意味著(zhù)可以避免以后的類(lèi)型轉換
List<String> wordlist = new ArrayList<String>();
Set<String> wordset = new HashSet<String>();
現在,您的 for/in 循環(huán)可以避開(kāi)老式的 Object,變得更加具體。清單 13 演示了這一點(diǎn):
清單 13. 在知道集合中的類(lèi)型時(shí),您的循環(huán)體可以更加具有類(lèi)型針對性
for (String word : wordlist) {
System.out.print(word + \" \");
}
作為一個(gè)更加完整的示例,清單 14 沿用了清單 10 所示的程序,并添加了一些通用的列表和更加具體的 for/in 循環(huán):
清單 14:可以利用泛型重寫(xiě)清單 10
package com.oreilly.tiger.ch07;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ForInDemo {
public static void main(String[] args) {
// These are collections to iterate over below
List<String> wordlist = new ArrayList<String>();
Set<String> wordset = new HashSet<String>();
// Basic loop, iterating over the elements of an array
// The body of the loop is executed once for each element of args[].
// Each time through, one element is assigned to the variable word.
System.out.println(\"Assigning arguments to lists...\");
for (String word : args) {
System.out.print(word + \" \");
wordlist.add(word);
wordset.add(word);
}
System.out.println();
// Iterate through the elements of the List now
// Since lists have an order, these words should appear as above
System.out.println(\"Printing words from wordlist \" +
\"(ordered, with duplicates)...\");
for (String word : wordlist) {
System.out.print((String)word + \" \");
}
System.out.println();
// Do the same for the Set. The loop looks the same but by virtue
// of using a Set, word order is lost, and duplicates are discarded.
System.out.println(\"Printing words from wordset \" +
\"(unordered, no duplicates)...\");
for (String word : wordset) {
System.out.print((String)word + \" \");
}
}
}
當然,在這些例子中,類(lèi)型轉換還沒(méi)有完全消失。但是,這些工作正逐步轉交給編譯器完成(如果您對這類(lèi)事情感興趣,那么可以說(shuō)這就是泛型或多或少要做的事)。在編譯的時(shí)候,所有這些類(lèi)型都會(huì )被檢測,您可能得到相應的錯誤信息。如果有人能做這項工作,那么,其他所有人也能這么做,不是嗎?
Who the heck is E?
如果您是 Java 老手,但是剛接觸 Tiger,那么所有對 E 的引用對您來(lái)說(shuō)可能很奇怪。這些都是與參數化類(lèi)型支持(泛型)有關(guān),它允許 Iterator 可以處理類(lèi)型化的集合 —— 例如,Iterator<String> 能處理這個(gè)新版本接口,敬請參閱 developerWorks 即將在 12 月 7 日推出的關(guān)于泛型的教程。
類(lèi)與 for/in 的集成
迄今為止,我只是針對 Java 事先打包的類(lèi)和類(lèi)型(array、list、map、set 和其他集合)進(jìn)行遍歷。盡管這已經(jīng)相當不錯,但編程語(yǔ)言的美麗在于它們能幫助您定義自己的類(lèi)。定制對象是大型應用程序的支柱。這一節要處理的只是允許 for/in 構造使用您自己的對象所涉及的一些概念與步驟。
一個(gè)新接口
到了現在,您應當熟悉 java.util.Iterator 接口了,倘若您不熟悉它,清單 15 演示了這個(gè)接口,而且是按照它在 Tiger 出現的形式演示的:
清單 15. Iterator 長(cháng)時(shí)間以來(lái)一直是 Java 語(yǔ)言的中流砥柱
package java.util;
public interface Iterator<E> {
public boolean hasNext();
public E next();
public void remove();
}
但是,為了利用 for/in,需要在您的域知識中添加另一個(gè)接口 java.lang.Iterable。該接口如清單 16 所示:
清單 16. Iterable 接口是 for/in 構造的基礎
package java.lang;
public interface Iterable<E> {
public java.util.Iterator<E> iterator();
}
是 java.lang,而不是 java.util
請注意,Iterable 位于 java.lang 之中,而不是位于java.util 中。至于為什么會(huì )這樣,我沒(méi)有找到任何明確的文檔,但就我個(gè)人猜測,可能是為了避免必須導入接口(java.lang 位于為所有 Java 代碼自動(dòng)導入的名稱(chēng)空間集中)。
為了讓您的對象或類(lèi)能與 for/in 一起工作,對象和類(lèi)需要實(shí)現 Iterable 接口。這留給您兩個(gè)基本場(chǎng)景:
* 擴展現有的、已經(jīng)實(shí)現了 Iterable(因此也就已經(jīng)支持 for/in)的集合類(lèi)。
* 手動(dòng)處理遍歷,定義自己的 Iterable 實(shí)現。
手動(dòng)處理遍歷
如果有可能,我極力建議您用定制對象擴展現有的集合。事情會(huì )變得極為簡(jiǎn)單,而您可以避免所有繁瑣的細節。清單 17 顯示了一個(gè)這樣做的類(lèi):
清單 17. 擴展現有的集合是利用 for/in 的捷徑
package com.oreilly.tiger.ch07;
import java.util.LinkedList;
import java.util.List;
public class GuitarManufacturerList extends LinkedList<String> {
public GuitarManufacturerList() {
super();
}
public boolean add(String manufacturer) {
if (manufacturer.indexOf(\"Guitars\") == -1) {
return false;
} else {
super.add(manufacturer);
return true;
}
}
}
因為 LinkedList 已經(jīng)可以使用 for/in,所以,不需要特殊的代碼,就可以在 for/in 中使用這個(gè)新類(lèi)。清單 18 演示了這點(diǎn),以及做到這一點(diǎn)需要做的工作是多么地少:
清單 18. Iterable 接口是 for/in 構造的基礎
package com.oreilly.tiger.ch07;
import java.io.IOException;
import java.io.PrintStream;
public class CustomObjectTester {
/** A custom object that extends List */
private GuitarManufacturerList manufacturers;
public CustomObjectTester() {
this.manufacturers = new GuitarManufacturerList<String>();
}
public void testListExtension(PrintStream out) throws IOException {
// Add some items for good measure
manufacturers.add(\"Epiphone Guitars\");
manufacturers.add(\"Gibson Guitars\");
// Iterate with for/in
for (String manufacturer : manufacturers) {
out.println(manufacturer);
}
}
public static void main(String[] args) {
try {
CustomObjectTester tester = new CustomObjectTester();
tester.testListExtension(System.out);
} catch (Exception e) {
e.printStackTrace();
}
}
}
手動(dòng)處理遍歷
在某些不常見(jiàn)的情況下 —— 老實(shí)說(shuō),我費了很大勁想到了很多 —— 在您的定制對象可以遍歷的時(shí)候,您可能需要執行特定的行為。在這些(相當不幸)的情況下,您必須自己處理這些事情。清單 19 演示了如何做,雖然需要做很多工作,但是并不復雜,所以我把代碼留給您自己來(lái)看。以下這個(gè)類(lèi)提供了文本文件的包裝器,在遍歷它的時(shí)候,它將列出文件中的每行內容。
清單 19. 耐心點(diǎn),您自己也能實(shí)現 Iterable 接口,并在循環(huán)中提供定制行為
package com.oreilly.tiger.ch07;
import java.util.Iterator;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
/**
* This class allows line-by-line iteration through a text file.
* The iterator\'s remove() method throws UnsupportedOperatorException.
* The iterator wraps and rethrows IOExceptions as IllegalArgumentExceptions.
*/
public class TextFile implements Iterable<String> {
// Used by the TextFileIterator below
final String filename;
public TextFile(String filename) {
this.filename = filename;
}
// This is the one method of the Iterable interface
public Iterator<String> iterator() {
return new TextFileIterator();
}
// This non-static member class is the iterator implementation
class TextFileIterator implements Iterator<String> {
// The stream being read from
BufferedReader in;
// Return value of next call to next()
String nextline;
public TextFileIterator() {
// Open the file and read and remember the first line
// Peek ahead like this for the benefit of hasNext()
try {
in = new BufferedReader(new FileReader(filename));
nextline = in.readLine();
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
// If the next line is non-null, then we have a next line
public boolean hasNext() {
return nextline != null;
}
// Return the next line, but first read the line that follows it
public String next() {
try {
String result = nextline;
// If we haven\'t reached EOF yet...
if (nextline != null) {
nextline = in.readLine(); // Read another line
if (nextline == null)
in.close(); // And close on EOF
}
// Return the line we read last time through
return result;
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
// The file is read-only; we don\'t allow lines to be removed
public void remove() {
throw new UnsupportedOperationException();
}
}
public static void main(String[] args) {
String filename = \"TextFile.java\";
if (args.length > 0)
filename = args[0];
for (String line : new TextFile(filename))
System.out.println(line);
}
}
其中大部分工作是實(shí)現 Iterator,然后通過(guò) iterator() 方法返回它。其他的事情就非常簡(jiǎn)單了。但是,您可以看到,與擴展一個(gè)現成的類(lèi)來(lái)完成同樣的工作相比,手動(dòng)實(shí)現 Iterable 接口需要做的工作多得多。
不能做什么
我確實(shí)認為 for/in 是這些好東西中的一個(gè),但是與所有的好東西一樣,它們也有自身的局限性。原因是 for/in 設置的方式,特別是因為它沒(méi)有顯式地使用 Iterator,所以使用這個(gè)新構造時(shí),有些事情是您不能做的。
定位
最明顯的顯然是不能確定您在列表或數組(或者定制對象)中的位置。為了提醒您,清單20 顯示了典型 for 循環(huán)的一個(gè)可能用法。請注意,索引變量不僅能是在列表中移動(dòng),還能指示其所在位置:
清單 20. 在普通的循環(huán)中使用迭代變量
List<String> wordList = new LinkedList<String>();
for (int i=0; i<args.length; i++) {
wordList.add(\"word \" + (i+1) + \": \'\" + args[i] + \"\'\");
}
這不是什么古怪的用法,而是很普通的編程方式。但是,您不能用 for/in 完成這個(gè)簡(jiǎn)單的任務(wù),如清單 21 所示:
清單 21. 不可能在 for/in 循環(huán)中訪(fǎng)問(wèn)位置
public void determineListPosition(PrintStream out, String[] args)
throws IOException {
List<String> wordList = new LinkedList<String>();
// Here, it\'s easy to find position
for (int i=0; i<args.length; i++) {
wordList.add(\"word \" + (i+1) + \": \'\" + args[i] + \"\'\");
}
// Here, it\'s not possible to locate position
for (String word : wordList) {
out.println(word);
}
}
在這里,沒(méi)有任何類(lèi)型的計數器變量(或者 Iterator),也不存在任何僥幸。如果需要定位,就得用“普通”的 for。清單 22 顯示了定位的另外一個(gè)常見(jiàn)用法 —— 處理字符串:
清單 22. 另一個(gè)問(wèn)題 —— 字符串連接
StringBuffer longList = new StringBuffer();
for (int i=0, len=wordList.size(); i < len; i++) {
if (i < (len-1)) {
longList.append(wordList.get(i))
.append(\", \");
} else {
longList.append(wordList.get(i));
}
}
out.println(longList);
刪除項目
另外一個(gè)限制是項目刪除。如清單 23 所示,在列表遍歷期間無(wú)法刪除項目:
清單 23. 在 for/in 循環(huán)中無(wú)法刪除項目
public void removeListItems(PrintStream out, String[] args)
throws IOException {
List<String> wordList = new LinkedList<String>();
// Assign some words
for (int i=0; i<args.length; i++) {
wordList.add(\"word \" + (i+1) + \": \" \'\" + args[i] + \"\'\");
}
// Remove all words with \"1\" in them. Impossible with for/in!
for (Iterator i = wordList.iterator(); i.hasNext(); ) {
String word = (String)i.next();
if (word.indexOf(\"1\") != -1) {
i.remove();
}
}
// You can print the words using for/in
for (String word : wordList) {
out.println(word);
}
}
從整體來(lái)看,這些不算什么限制,只是什么時(shí)候使用 for、什么時(shí)候使用 for/in 的一個(gè)準則??赡苁且恍┎恢狄惶岬募毠?。
最糟糕的結果是您可能找不到需要 for/in 的地方,這也正是我所擔心的。請記住,for/in 是一項很方便的功能,它能讓代碼更清晰、更簡(jiǎn)潔,同時(shí)也能讓代碼簡(jiǎn)潔得讓人頭痛。

