Как реализовать итератор как атрибут класса в Java - PullRequest
4 голосов
/ 26 декабря 2010

скажем, у меня есть этот простой класс MyArray с двумя простыми методами: add, delete и итератор. В основном методе мы можем видеть, как он должен использоваться:

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 должен перебирать arr из MyArray class, от start до end values; оба являются также атрибутами MyArray . Итак, как MyIterator должен использовать атрибуты MyArray , как должен реализовываться MyIterator? Возможно, я могу отправить текущий объект в инициализации:

it=new myIterator(this);

Но я думаю, это не лучшая душа. Или, может быть, сам MyArray должен реализовывать интерфейс Iterator? Как это решается?

EDIT:

Хорошо, спасибо всем. Это был простой пример того, что я хотел сделать, так что мне не нужен массив фиксированной длины. Что я действительно хочу сделать, это круговой FIFO, поэтому start и end являются курсорами.

Этот круговой 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

}

Но я бы хотел не беспокоиться об этих вещах и просто выполнить итерацию простым способом. Я могу сделать это с помощью интерфейса итератора, но тогда у меня есть 2 проблемы: первая, которую я уже объяснил и была решена многими ответами, и второе - мой массив состоит из пар целых чисел, и я не могу использовать итератор с примитивными типами.

Ответы [ 6 ]

8 голосов
/ 26 декабря 2010

Очень необычно поддерживать итератор в качестве переменной экземпляра класса.Вы можете пройти массив только один раз - вероятно, не то, что вы хотите.Скорее всего, вы хотите, чтобы ваш класс предоставлял итератор любому, кто хочет пройти через ваш массив.Ниже приведен более традиционный итератор.

Код Java 5+ - я не пытался компилировать или запускать, поэтому он может содержать ошибки (сейчас не рядом с машиной разработчика)Он также использует autobox'ing для конвертации 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.

4 голосов
/ 26 декабря 2010

По моему мнению, лучше реализовать MyArray как обычный Итерируемый объект, чтобы его можно было использовать в операторе 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
    }
}
2 голосов
/ 26 декабря 2010

Я предлагаю позволить MyArray реализовать интерфейс java.lang.Iterable и создать экземпляр итератора для вызова iterator() (в качестве анонимного класса). Затем вы можете использовать экземпляр MyArray непосредственно в конструкции foreach:

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. Таким образом, вы можете создать несколько экземпляров итераторов, которые не мешают друг другу.

2 голосов
/ 26 декабря 2010

Итератор - это интерфейс. Iterator<E>, что означает только Объект может идти сюда (E). Iterator<Integer> допустимо, но Integer<int> - не потому, что int является примитивным типом данных

Вы можете изменить массив на ArrayList и затем выполнить итерацию по этому массиву. Я добавил getIterator() метод, который возвращает arraylist.iterator() и протестировал его в 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());
  }

 }

}
1 голос
/ 26 декабря 2010

EDIT: это не работает для массивов примитивных типов: вы можете использовать Arrays для этого:

it = new Arrays.asList(arr).subList(start, end).iterator(); END OF EDIT

Если вы действительно хотите реализовать свой собственный итератор,Я бы предложил внутренний класс в этом сценарии.Таким образом, вы можете получить доступ к MyArray.this из myIterator.

public class MyArray {
    ....
    private class myIterator implements Iterator{
        ....
    }
}
0 голосов
/ 26 декабря 2010

MyArray должен реализовывать Iterator, поскольку он также отвечает за поддержание массива.Простой принцип инкапсуляции.

...