2011-12-18 60 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