【问题标题】:How to implement iterator as an attribute of a class in Java如何在Java中将迭代器实现为类的属性
【发布时间】:2010-12-26 11:18:59
【问题描述】:

假设我有一个简单的 MyArray 类,它有两个简单的方法:添加、删除和迭代器。在 main 方法中,我们可以看到它应该如何使用:

public class MyArray {
int start;
int end;
int[] arr;
myIterator it;
public MyArray(){
    this.start=0;
    this.end=0;
    this.arr=new int[500];
    it=new myIterator();
}
public void add(int el){
    this.arr[this.end]=el;
    this.end++;
}
public void delete(){
    this.arr[this.start]=0;
    this.start++;
}

public static void main(String[] args){
    MyArray m=new MyArray();

    m.add(3);
    m.add(299);
    m.add(19);
    m.add(27);
    while(m.it.hasNext()){
        System.out.println(m.it.next());
    }
}

然后应该以某种方式实现 MyIterator:

import java.util.Iterator;

public class myIterator implements Iterator{

@Override
public boolean hasNext() {
    // TODO Auto-generated method stub
    return false;
}

@Override
public Object next() {
    // TODO Auto-generated method stub
    return null;
}

@Override
public void remove() {
    // TODO Auto-generated method stub

}

}

MyIterator 应该从 MyArray 类中迭代 arr,从 startend 值;两者也是 MyArray 的属性。那么,既然 MyIterator 应该使用 MyArray 属性,那么 MyIterator 应该如何实现呢?也许我可以在初始化时发送当前对象:

it=new myIterator(this);

但我想这不是最好的灵魂。或者也许 MyArray 本身应该实现 Iterator 接口?这是怎么解决的?

编辑:

好的,谢谢大家。这是我要做的一个简单示例,所以不要关心固定长度的数组。我真正想做的是循环 FIFO,这就是为什么 startend 是光标。

此循环 FIFO 将是一个整数对数组,例如大小为 300:int[][] arr=new int[300][2]

在迭代循环数组时,我必须注意计数器是否到达末尾并使其从头开始,所以这就是我解决它的方法:

if  (this.start >= this.end )   temp_end=this.end+this.buff.length; 
else    temp_end=this.end;
int ii;
int j=0;
int[] value=new int[2];
for(int i=this.start; i<temp_end; i++){
    ii=i% this.arr.length;
    value=this.buff[ii]; 
    //do anything with value

}

但是我想避免担心这些事情,只是以一种简单的方式进行迭代,我可以使用迭代器接口来做到这一点,但是我有两个问题:第一个我已经解释过并且已经被许多答案解决了,第二个是我的数组是由成对的整数组成的,我不能将迭代器与原始类型一起使用。

【问题讨论】:

    标签: java iterator


    【解决方案1】:

    将迭代器维护为类的实例变量是非常不寻常的。您只能遍历数组一次 - 可能不是您想要的。更有可能的是,您希望您的类为想要遍历您的数组的任何人提供一个迭代器。下面是一个更传统的迭代器。

    Java 5+ 代码 - 我没有尝试编译或运行,所以它可能包含错误(现在不在开发机器附近)。它还使用自动装箱将Integer 转换为int

    public class MyArray implements Iterable<Integer> {
    
        public static class MyIterator implements Iterator<Integer> {
    
            private final MyArray myArray;
            private int current;
    
            MyIterator(MyArray myArray) {
                this.myArray = myArray;
                this.current = myArray.start;
            }
    
            @Override
            public boolean hasNext() {
                return current < myArray.end;
            }
    
            @Override
            public Integer next() {
                if (! hasNext())   throw new NoSuchElementException();
                return myArray.arr[current++];
            }
    
            @Override
            public void remove() {
                // Choose exception or implementation: 
                throw new OperationNotSupportedException();
                // or
                //// if (! hasNext())   throw new NoSuchElementException();
                //// if (currrent + 1 < myArray.end) {
                ////     System.arraycopy(myArray.arr, current+1, myArray.arr, current, myArray.end - current-1);
                //// }
                //// myArray.end--;
            }
        }
    
        ....
    
        // Most of the rest of MyArray is the same except adding a new iterator method ....
    
        public Iterator<Integer> iterator() {
            return new MyIterator();
        }
    
        // The rest of MyArray is the same ....
    
    }
    

    另请注意:请注意不要达到静态数组的 500 个元素限制。如果可以,请考虑使用 ArrayList 类。

    【讨论】:

    • WTF?为什么要将迭代器实现为静态类?您正在创建不必要的全局范围,这可能会导致一些严重的线程安全问题。此外,通过将 myArray 声明为 final,您引入了一个不必要的约束,使得在最初创建数组后无法调整其大小。
    • 您建议使用 ArrayList 类,但 ArrayList 的核心是一个在内部使用可调整大小数组的 List 实现。通过一些工作,这个类可以很容易地在内部使用可调整大小的数组进行扩展。我会考虑投反对票,但我宁愿提供更好的答案。
    【解决方案2】:

    在我看来,最好将 MyArray 实现为常见的Iterable 对象,这样它就可以在 for 语句中使用。

    我的建议:

    /**
     * My array
     */
    public class MyArray<TItem> implements Iterable<TItem>
    {
        /**
         * Internal used iterator.
         */
        private class MyArrayIterator<TItem> implements Iterator<TItem>
        {
            private MyArray<TItem> _array;
    
            /**
             * @param array The underlying array.
             */
            public MyArrayIterator(MyArray<TItem> array)
            {
                this._array = array;
            }
    
            /**
             * Gets the underlying array.
             * 
             * @return The underlying array.
             */
            public MyArray<TItem> getArray() {
                return this._array;
            }
    
            @Override
            public boolean hasNext() {
                // TODO Auto-generated method stub
                return false;
            }
    
            @Override
            public TItem next() {
                // TODO Auto-generated method stub
                return null;
            }
    
            @Override
            public void remove() {
                // TODO Auto-generated method stub
    
            }
    
        }
    
        public void add(int el){
            // do add
        }
    
        public void delete(){
            // do delete
        }
    
        @Override
        public Iterator<TItem> iterator() {
            // TODO Auto-generated method stub
            return new MyArrayIterator<TItem>(this);
        }
    }
    

    正如我所说,您可以在 for 语句中使用它:

    private static void test(MyArray<String> strArray)
    {
        for (String str: strArray) {
            // do something
        }
    }
    

    【讨论】:

    • 如果我的 TItem 应该是一个整数数组 int[],而不是一个字符串,该怎么办?这是原始类型,所以我不能将其用作参数
    • @de3 你不能用原始类型实现 Iterator 或 Iterable 。迭代器只支持对象。
    【解决方案3】:

    Iterator 是一个接口。 Iterator&lt;E&gt; 这意味着 只有 Object 可以到这里 (E) 。 Iterator&lt;Integer&gt; 是合法的,但 Integer&lt;int&gt; 不是因为 int 是原始数据类型

    您可以将数组更改为ArrayList,然后遍历此数组列表。我添加了返回arraylist.iterator()getIterator() 方法并在main() 方法中对其进行测试

    import java.util.ArrayList;
    import java.util.Iterator;
    
    public class MyArray {
     int start;
     int end;
     ArrayList<Integer> arr;
    
    
     public MyArray() {
      this.start = 0;
      this.end = 0;
      arr = new ArrayList<Integer>(500);
     }
    
     public void add(int el) {
      arr.add(el);
      this.end++;
     }
    
     public void delete() {
      arr.remove(arr.size()-1);
      this.start++;
     }
    
     public Iterator<Integer> getIterator(){
      return arr.iterator();
     }
    
     public static void main(String[] args) {
      MyArray m = new MyArray();
    
      m.add(3);
      m.add(299);
      m.add(19);
      m.add(27);
    
      Iterator<Integer> it = m.getIterator();
    
      while(it.hasNext()){
       System.out.println(it.next());
      }
    
     }
    
    }
    

    【讨论】:

      【解决方案4】:

      我的建议是让MyArray 实现接口java.lang.Iterable 并为每个iterator() 调用(作为匿名类)创建一个迭代器的实例。然后你可以直接在foreach 构造中使用MyArray 的实例:

      public class MyArray implements Iterable {
      
        // ...
      
        // Only arr is needed now as an instance variable.
        // int start;
        // int end;
        int[] arr;
        // myIterator it;
      
        /**
         *  From interface Iterable.
         */
        public Iterator<Integer> iterator() {
      
          return new Iterator<Integer>() {
            // The next array position to return
            int pos = 0;
      
            public boolean hasNext() {
              return pos < arr.length;
            }
      
            public Integer next() {
              if(hasNext()) 
                return arr[pos++];
              else
                throw new NoSuchElementException();
            }
      
            public void remove() {
              throw new UnsupportedOperationException();
            }
          }
        }
      
      
      }
      

      更新:根据 BertF 的评论,我更新了我的代码以明确说明 MyArray 类的唯一实例变量现在是 arr。迭代器的状态现在位于匿名 Iterator 实现中。因此,您可以创建多个互不干扰的迭代器实例。

      【讨论】:

      • 我注意他的实例变量。
      • 我更新了我的代码,强调不需要原来的startendit实例变量。
      • start 是他的 deletes 和 end 是他的 adds。没有它们,他每次添加和删除数组时都必须重新分配/调整数组大小。
      【解决方案5】:

      编辑:这不适用于原始类型的数组: 您可以为此使用数组:

      it = new Arrays.asList(arr).subList(start, end).iterator(); 编辑结束

      如果您真的想实现自己的迭代器,我建议在这种情况下使用内部类。这样您就可以从 myIterator 访问 MyArray.this。

      public class MyArray {
          ....
          private class myIterator implements Iterator{
              ....
          }
      }
      

      【讨论】:

        【解决方案6】:

        MyArray 应该实现 Iterator,因为它还负责维护数组。简单的封装原理。

        【讨论】:

          猜你喜欢
          • 2012-02-18
          • 2017-08-05
          • 2013-01-19
          • 1970-01-01
          • 2018-03-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多