【问题标题】:Why is it not possible for looping a class that directly implements Iterator?为什么不能循环直接实现Iterator的类?
【发布时间】:2013-05-31 20:59:54
【问题描述】:

我的问题是:是否可以仅通过实现Iterator 来通过容器,即不实现Iterable

是否可以这样做:

//From somewhere
Container c = new Container();
//I add elements to the list in the c object.

for(String s : c){
    //Warning: It is not applicable for this expression...
}


class Container implements Iterator<String>{
    //Here I implement a single linked list

    //Here I implement all of the methods of Iterator
}

【问题讨论】:

  • docs.oracle.com/javase/7/docs/api/java/util/Iterator.html -> 我看不出哪里需要 Iterable。如果您想将容器扔到 foreach 中,那么它确实是必需的。但这不是使用 Iterator 进行迭代的唯一方法。
  • 可以实现Iterator所有你想要的。但是,使用 foreach 循环需要您实现 Iterable。另请注意,如果您实现 Iterator "only",您实际上只会在每个实例中循环一次。
  • @Fildor 我知道。但后来我得到答案,Iterable 需要能够为每个循环做一个。我知道:)
  • 另外你需要从实现Iterable&lt;E&gt;接口的类中调用Container对象,使用Iterable&lt;E&gt;#iterator()方法
  • 在看到对您问题的修改后,它变得更加清晰。您真正要问的是您是否能够仅通过Iterator 使用enhanced for 循环。那么明确的答案是“不”。为什么?将问题直接交给功能的设计者。例如,乔什·布洛赫。

标签: java for-loop iterator iterable


【解决方案1】:

尽管这种设计很荒谬,但为了招待你的问题,你可以这样写:

public class Container implements Iterable<String>, Iterator<String> {
  @Override public Iterator<String> iterator() { return this; }

  ... everything else you envision for your class ...
}

然后你就可以使用了

for (String s : container)

您可以将其写成学习经验,但我强烈建议您永远不要在实际项目中尝试它。反对它的主要原因是期望iterator 方法总是返回一个新的、独立的迭代器,而这个实现却相反。举一个具体的例子,假设有人需要用自己迭代容器的笛卡尔积:

for (String s1 : container)
  for (String s2 : container) 
     processPair(s1, s2);

每个 Java 程序员都会告诉你,毫无疑问,processPair 将被容器中的每个可能对调用。他们会从你的班级看到的行为会让他们头晕目眩。如果生产中的错误被追踪到这个实现,你的队友会......让我们说他们不会欣赏它。

【讨论】:

    【解决方案2】:

    IterableIterator 之间存在逻辑差异。

    • Iterable 是可以迭代的集合或数据结构。它是一个数据持有者。

    • Iterator 是定义迭代逻辑的对象(以何种方式和顺序循环通过Iterable 的元素)。它可以(并且大多数时候应该)与数据结构或集合的逻辑分开。目的是能够为同一个 Iterable 定义多个 Iterator 实现。
      我错过了但存在于其他答案中的一个重要点是使循环状态远离可迭代对象。这允许同时循环通过同一个集合,因为每个循环都将使用一个单独的迭代器实例。

    一个简单的例子是写两个迭代器到ArrayList,一个向前循环,另一个向后循环。您只需在现有类 (ArrayList) 上创建 Iterator 实现,而无需修改后者。因此,可以只实现一个迭代器。

    然而,Java 语言中的for 循环结构被设计为与可迭代对象一起使用。代码:

    for (Object o : collecton) { 
        ...
    }
    

    由编译器翻译成类似:

    Iterator i = collection.iterator();
    while (i.hasNext()) {
      ....
    }
    

    因此,迭代器本身不能以标准方式与for 循环一起使用。或者,您可以像这样使用它:

    for (Iterator i = collection.iterator(); i.hasNext(); ) {
        Object current = i.next();
        ....
    }
    

    【讨论】:

    • +1,我认为OP可能会将Iterable和Iterator这两个接口混淆
    【解决方案3】:

    容器上的两次连续迭代怎么样:

    • 通过直接实现Iterator,由于变量的迭代器将被所有客户端共享,您将无法轻松地多次迭代您的集合...

    • 通过定义一个方法Iterator:(就像AbstractList上的那个):

      公共迭代器 iterator() { 返回新的 Itr(); //每次新建实例 }

      每次创建Iterator 的新实例允许您对其进行多次迭代。 (当然,最好不要同时)。

    此外,它避免了您违反单一职责原则,因为迭代容器的方式代表了“定义”容器之外的另一个目标。

    【讨论】:

      【解决方案4】:

      即使你有你的class implements Iterator&lt;T&gt; 为了得到它iterator() 你必须实现iterable 并返回你的iterator

      public class MyCollection<E> implements Iterable<E>{
      
          public Iterator<E> iterator() {
              return new MyIterator<E>();
          }
      }
      

      这里是MyIterator类对应的实现骨架:

      public class MyIterator <T> implements Iterator<T> {
      
          public boolean hasNext() {
      
              //implement...
          }
      
          public T next() {
              //implement...;
          }
      
          public void remove() {
              //implement... if supported.
          }
      }
      

      【讨论】:

        【解决方案5】:

        为了使用enhanced for-loop(又名花哨的for循环),您必须实现Iterable而不是Iterator

        当然你可以实现Iterator,在这种情况下你的容器就是Iterator

        public class Cont<T> implements Iterator<T> {
        
            @Override
            public boolean hasNext() {
                // TODO implment
                return false;
            }
        
            @Override
            public T next() {
                // TODO implment
                return null;
            }
        
            @Override
            public void remove() {
                // TODO implment
            }
        
        }
        

        你可以这样使用它:

        public static void main(String... args) {
            Cont cont = new Cont<SomeType>();
            while(cont.hasNext()) {
                SomeType obj = cont.next();
            }
        }
        

        通常的情况是实现Iterable,因此您可以使用所有可用的语言功能。如果您正在编写 Collection 代码,或者您希望在其某些方法中包含一些逻辑,例如 lazy loadingnext() 方法中,请实现一个迭代器。

        【讨论】:

          【解决方案6】:

          如果您想使用简单的 for-each 语句 - 。您需要使用iterator() 方法从Iterable&lt;E&gt; 对象调用Iterator&lt;E&gt; 对象。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-03-02
            • 1970-01-01
            • 1970-01-01
            • 2018-08-05
            • 2010-11-18
            • 2017-03-23
            • 1970-01-01
            • 2010-09-29
            相关资源
            最近更新 更多