2011-12-18 58 views
3

可能重複:
Why is Java's Iterator not an Iterable?爲什麼擴展for循環對迭代器不起作用?

我們都知道Java的擴展for循環:

List<X> list = ... 
for(X x : list) { ... } 

我需要的是:

Iterator<X> listIterator = ... 
for(X x : listIterator){ ... } 

Java不允許這樣做。 我想知道規範不支持這個是否有充分的理由。

這裏是我的用例:

我正在寫一類文件格式的閱讀器。這樣的文件包含要讀取的條目。爲了這個例子,假設我試圖重新創建BufferedReader並且我的元素是字符串。

我與原BufferedReader的API風格,這迫使我寫醜陋的代碼非常不高興,如:

for(String line = reader.readLine(); line != null; line = reader.readLine(){ 
    ... 
} 

我寧願有一些不錯的像

for(String line : reader){ 
    ... 
} 

當然我可以讓我的BufferedReader執行Iterable<String>。但這意味着可能會多次調用一個iterator()方法。由於我無法在文件中查找,因此我無法真正支持多個並行迭代器。

有我的BufferedReader執行Iterator<String>而不是Iterable<String>似乎更合理。但我不能使用for聲明:-(

+0

您是否檢查過高級循環是否多次調用「迭代器」方法?我會認爲它只做一次.. – 2011-12-18 21:28:10

+1

標題問題的答案是「因爲這是語言的定義方式」。 – 2011-12-18 21:31:06

+2

同樣,我不同意它是重複的。我非常瞭解這兩個接口的有意義的區別。我在問爲什麼這個差異與for循環有關。我也知道它是這樣指定的。我想知道爲什麼這樣指定。 – Stefan 2011-12-18 21:32:27

回答

1

因爲對於foreach循環的合同遍歷元素,如果被允許只取一個Iterator,你可以給它一個半消耗Iterator,它不會知道其中的差別。

Set<String> set = new HashSet(Arrays.asList(new String [] {"One", "Two", "Three"})); 
Iterator i = set.iterator(); 
// Consume the first. 
String first = i.next(); 
for (String s : i) { // Error here. 

} 

我接受它會讓事情變得簡單,但它也可能會引入一些嚴重的錯誤,因爲您不知道您已涵蓋集合中的所有條目。

// Works fine because Set is Iterable and the contract is satisfied 
// that this will iterate over <b>all</b> of the entries. 
for (String s : set) { 
} 

你總是可以通過製作一個短暫的Iterable來破解它。我現在不能測試,但像這樣:

for (String s : new Iterable { 
    public iterator<String>() { 
    return set.iterator(); 
    } 
}) { 
} 

繁瑣,但它應該工作。

+0

你可以使用靜態方法更清晰地實現你的破解 - 我剛剛添加了一個到[相關問題](http://stackoverflow.com/a/8555153/304) – McDowell 2011-12-18 22:02:55

+0

好點保羅。雖然語法優雅,但在文件中間開始時,foreach的語義會發生變化。由於從不可查找的流中讀取無法跟上foreach語義,即使在解決方法時使用它也是錯誤的。 – Stefan 2011-12-18 22:06:39

+0

@McDowell - 謝謝...我現在在一臺機器上沒有Java 5,所以我無法測試它。這很聰明,你如何使它成爲一次只有'Iterator'。 @Lawnmower - 強調濫用foreach循環是錯誤的,特別是通過返回'this'來讓你的'Iterator'' Iterable'是非常重要的。這是充滿危險的。感謝您的提醒。 – OldCurmudgeon 2011-12-18 22:46:45

1

當然你也可以支持多個並行的迭代器,你可以打開該文件,曾多次。

class BufferedReader() implements Iterable<String> { 
    String fileName; 
    InputStreamReader in; 

    public BufferedReader(String fileName) { 
     this.fileName = fileName; 
     in = new InputStreamReader(new FileInputStream(fileName)); 
    } 
    public Iterator<String> iterator() { 
     return new Iterator<String>() { 
      BufferedReader in = new BufferedReader(fileName); 
      String nextLine = in.readLine(); 
      public boolean hasNext() { 
       return nextLine != null; 
      } 
      public String next() { 
       String str = nextLine; 
       nextLine = in.readLine(); 
       return str; 
      } 
     } 
    } 
} 
+0

你沒有處理'IOException' – McDowell 2011-12-18 21:57:56

+0

這是一個好主意。我應該指定我沒有真正從文件讀取。我只有一個InputStream開始。例如,當您直接從網絡套接字讀取時。 – Stefan 2011-12-18 22:22:10

+0

您也可以將所有數據讀入一個ArrayList,然後您的BufferedReader從該列表讀取數據。然後你可以尋求。或者你只是實現Iterable。 for循環只會調用一次itarator方法。所以應該沒有問題。 – SpiderPig 2011-12-18 23:53:29