【问题标题】:Why does the extended for loop not work for iterators? [duplicate]为什么扩展的 for 循环不适用于迭代器? [复制]
【发布时间】:2012-01-23 04:55:00
【问题描述】:

可能重复:
Why is Java's Iterator not an Iterable?

我们都知道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&lt;String&gt;。但这意味着有一个iterator() 方法可能会被多次调用。由于我无法在文件中查找,因此我无法真正支持多个并行迭代器。

让我的BufferedReader 实现Iterator&lt;String&gt; 而不是Iterable&lt;String&gt; 似乎更合理。但是后来我不能使用for 语句:-(

【问题讨论】:

  • 你检查过高级循环是否多次调用“迭代器”方法吗?我会假设它只做一次..
  • 标题问题的答案是“因为这是定义语言的方式”。
  • 再一次,我不同意它是重复的。我非常清楚这两个接口的概念差异。我在问为什么这种差异与 for 循环有关。我也知道它是这样指定的。我想知道为什么以这种方式指定它。
  • @coding.mof 按照规范只调用一次。
  • 扩展的 for 循环和带有迭代器的 for 循环之间没有有效的区别。扩展的 for 循环 for(X x : myList) 只是 for(Iterator&lt;X&gt; it = myList.iterator(); it.hasNext(); ){X x=it.next();...} 的较短版本。在您的情况下,它会尝试从迭代器中获取迭代器(这没有意义)。但针对您的用例:尝试String line=null; while((line = reader.readLine()) != null){ ... }。如果你不知道while循环的行数更合乎逻辑

标签: java for-loop


【解决方案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 ();
  }
} ) {
}

很麻烦,但应该可以。

【讨论】:

  • 您可以使用静态方法更清晰地实现您的 hack - 我刚刚在 related question 中添加了一个
  • 好点保罗。虽然语法优雅,但从文件中间开始时,foreach 的语义会发生变化。由于从不可搜索的流中读取无法跟上 foreach 语义,因此即使在变通的情况下使用它也是错误的。
  • @McDowell - 谢谢......我现在在一台没有 java 5 的机器上,所以我无法测试它。这很聪明,你如何使它成为一个只有一次的Iterator。 @Lawnmower - 重要的是要强调滥用 foreach 循环是错误的,尤其是通过返回 this 使您的 Iterator Iterable 成为错误。那是充满危险的。谢谢提醒。
【解决方案2】:

当然,您可以支持多个并行迭代器。您可以多次打开文件。

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;
            }
        }
    }
}

【讨论】:

  • 你没有处理IOException
  • 这是个好主意。我应该指定我并没有真正从文件中读取。我只有一个 InputStream 开始。例如,当您直接从网络套接字读取时就是这种情况。
  • 您也可以将所有数据读入一个 ArrayList,然后您的 BufferedReader 从该列表中读取。然后你可以寻找。或者你只是实现 Iterable 。 for 循环只会调用一次 iterator 方法。所以应该没有问题。
猜你喜欢
  • 2018-07-05
  • 1970-01-01
  • 2011-07-27
  • 2014-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多