2012-11-20 8 views
15

Mam listę właściwości, która musi wielokrotnie powtarzać iterację. Używam go do śledzenia serii stron w przepływie pracy, który zostanie utworzony dynamicznie. To nie zachowuje się tak, jak bym się spodziewał. Biorąc pod uwagę ten przykład:Używanie ListIterator do przechodzenia w przód iw tył przez obiekt LinkedList w Javie

LinkedList<String> navigationCases; 
navigationCases.add("page1"); 
navigationCases.add("page2"); 
navigationCases.add("page3"); 
navigationCases.add("page4"); 

ListIterator navigationItr = navigationCases.listIterator(); 
navigationItr.next(); // Returns page1 
navigationItr.next(); // Returns page2 
navigationItr.previous(); //Returns page2 again 
navigationItr.next(); //Returns page2 again 

Myślałem, że może ja budowałem moją listę nieprawidłowo lub przy użyciu iteracyjnej złego, ale po przeczytaniu dokumentacji, to wydaje się być zgodne z projektem:

ListIterator nie ma prądu element; jego pozycja kursora zawsze leży między elementem, który zostanie zwrócony przez wywołanie poprzedniego() i elementem, który zostanie zwrócony przez wywołanie next().

I:

(Next) Zwraca następny element na liście. Ta metoda może być wielokrotnie wywoływana w celu iteracji na liście lub wymieszania z wywołaniami poprzednich w celu powrócenia do poprzedniej. (Zwróć uwagę, że naprzemienne wywoływanie do następnego i poprzedniego spowoduje wielokrotne zwracanie tego samego elementu).

Po przeczytaniu tego, jest jasne, dlaczego mój kod zachowuje się w taki sposób. Po prostu nie rozumiem, dlaczego to powinno działać w ten sposób. Nawet usunięcie wydaje się odgiąć w tył, aby pomieścić tę implementację:

Należy zauważyć, że metody remove() i set (Object) nie są zdefiniowane pod względem położenia kursora; są zdefiniowane tak, aby działały na ostatnim elemencie zwróconym przez wywołanie next() lub previous().

Koncepcyjnie, LinkedList wydawało się modelować moje przypadki przepływu pracy całkiem dobrze, ale nie mogę używać Iteratora, który zachowuje się w ten sposób. Czy tu coś pomijam, czy powinienem napisać własną klasę, utrzymywać listę przypadków i poruszać się po nich?

+1

Dlaczego nie możesz "użyć iteratora, który zachowuje się w ten sposób"? Reszta świata ma. Po prostu zaakceptuj, jak działa i wykorzystaj go tak, jak zamierzano go użyć. – Madbreaks

+2

Zachowuje się w ten sposób, ponieważ wykonuje iterację i usuwanie rozpoczynając od pracy końcowej dokładnie tak samo, jak iterowanie i usuwanie, zaczynając od początku. –

+0

@Madbreaks W jaki sposób ten przypadek użycia różni się od sposobu, w jaki był "przeznaczony do użycia"? Jak mogę uwzględnić "specjalny" przypadek pytania o poprzedni element natychmiast po otrzymaniu prośby o następny? Lub pytając o następny zaraz po zapytaniu o poprzednie? – user1535568

Odpowiedz

9

To powinno wykonywać swoją pracę:

public class Main { 
    public static void main(String[] args) { 
     final LinkedList<String> list = new LinkedList<String>(); 

     list.add ("1"); list.add ("2"); list.add ("3"); list.add ("4"); 

     final MyIterator<String> it = new MyIterator (list.listIterator()); 

     System.out.println(it.next()); 
     System.out.println(it.next()); 
     System.out.println(it.next()); 
     System.out.println(it.previous()); 
     System.out.println(it.previous()); 
     System.out.println(it.next()); 
    } 

    public static class MyIterator<T> { 

     private final ListIterator<T> listIterator; 

     private boolean nextWasCalled = false; 
     private boolean previousWasCalled = false; 

     public MyIterator(ListIterator<T> listIterator) { 
      this.listIterator = listIterator; 
     } 

     public T next() { 
      nextWasCalled = true; 
      if (previousWasCalled) { 
       previousWasCalled = false; 
       listIterator.next(); 
      } 
      return listIterator.next(); 
     } 

     public T previous() { 
      if (nextWasCalled) { 
       listIterator.previous(); 
       nextWasCalled = false; 
      } 
      previousWasCalled = true; 
      return listIterator.previous(); 
     } 

    } 
} 

I fiddle dla tego .

+0

Tak, nie widzę żadnego sposobu na zapamiętanie poprzedniej operacji. Dzięki! – user1535568

-1

coś jak to zrobić (Pseudokod) -

class SkipIterator extends ListIterator { 

    public E previous(){ 
     E n = super.previous(); 
     return super.previous(); 
    } 

    ... 

} 

następnie:

LinkedList<String> navigationCases; 
navigationCases.add("page1"); 
navigationCases.add("page2"); 
navigationCases.add("page3"); 
navigationCases.add("page4"); 

SkipIterator navigationItr = (SkipIterator)navigationCases.listIterator(); 
navigationItr.next(); // Returns page1 
navigationItr.next(); // Returns page2 
navigationItr.previous(); // Returns page1 

Cheers

+1

Niestety, chcę tylko wywołać previous() dwa razy iff poprzednia operacja była następna(). Jeśli następny nie był nazywany wcześniej, dwukrotne wywołanie previous() spowoduje, że iterator pominie element na liście. – user1535568

+0

Ok, więc zbuduj tą logikę. :) Pomysł polega na tym, że możesz rozszerzyć klasę podstawową i uwzględnić logikę, która jest potrzebna do użycia. – Madbreaks

+0

Tak, masz rację. Dzięki! – user1535568

0

ListIterator zaprojektowano tak, aby zachowywał się w ten sposób. Zobacz rozmowę pod odpowiedzią ShyJ na uzasadnienie.

Uważam to zachowanie się poza idiotyczne, i zamiast napisany bardzo prostą alternatywę. Oto kod Kotlin z funkcją rozszerzenia dla ArrayLists:

class ListIterator<E>(var list: ArrayList<E>) : Iterator<E> { 

    private var cursor: Int = 0 

    fun replace(newList: ArrayList<E>) { 
     list = newList 
     cursor = 0 
    } 

    override fun hasNext(): Boolean { 
     return cursor + 1 < list.size 
    } 

    override fun next(): E { 
     cursor++ 
     return current() 
    } 

    fun hasPrevious(): Boolean { 
     return 0 <= cursor - 1 
    } 

    fun previous(): E { 
     cursor-- 
     return current() 
    } 

    fun current(): E { 
     return list[cursor] 
    } 

} 

fun <E> ArrayList<E>.listFlippingIterator() = ListIterator(this) 

Jeśli chcesz włączyć funkcję usuwania, bardzo polecam pisanie API jawnie nakazać iterator czy powinien usunąć lewo lub w prawo, na przykład przez zdefiniowanie tych metod jako removeNext() i i .

Powiązane problemy