parte - Uso de ListIterator para moverse de un lado a otro en una lista enlazada en Java
listas en java (3)
Tengo una lista enlazada sobre la que necesito iterar varias veces. Lo estoy utilizando para realizar un seguimiento de una serie de páginas en un flujo de trabajo que se creará de forma dinámica. Esto no se comporta como yo esperaría. Dado este ejemplo:
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
Pensé que tal vez estaba construyendo mi lista incorrectamente, o usando el iterador de forma incorrecta, pero después de leer la documentación, esto parece ser por diseño:
Un ListIterator no tiene ningún elemento actual; su posición del cursor siempre se encuentra entre el elemento que sería devuelto por una llamada a previous () y el elemento que sería devuelto por una llamada a next ().
Y:
(Siguiente) Devuelve el siguiente elemento de la lista. Este método puede llamarse repetidamente para iterar a través de la lista, o entremezclarse con llamadas a la anterior para ir y venir. (Tenga en cuenta que alternar las llamadas a la siguiente y la anterior devolverá el mismo elemento repetidamente).
Entonces, después de leer esto, está claro por qué mi código se comporta de la manera en que lo hace. Simplemente no entiendo por qué debería funcionar de esta manera. Incluso eliminar parece estar doblando hacia atrás para acomodar esta implementación:
Tenga en cuenta que los métodos remove () y set (Object) no están definidos en términos de la posición del cursor; están definidos para operar en el último elemento devuelto por una llamada a next () o previous ().
Conceptualmente, una lista enlazada parecía modelar mis casos de flujo de trabajo bastante bien, pero no puedo usar un iterador que se comporte de esta manera. ¿Me estoy perdiendo algo aquí, o debo escribir mi propia clase para mantener una lista de casos y navegar a través de ellos?
Esto debería hacer tu trabajo:
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();
}
}
}
Y un fiddle para ello.
Haz algo como esto (pseudocódigo) -
class SkipIterator extends ListIterator {
public E previous(){
E n = super.previous();
return super.previous();
}
...
}
entonces:
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
Aclamaciones
ListIterator fue diseñado para comportarse de esta manera. Vea la conversación debajo de la respuesta de ShyJ para el razonamiento.
Encuentro que este comportamiento está más allá de lo idiota, y en cambio he escrito una alternativa muy simple. Aquí está el código de Kotlin con una función de extensión para 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)
Si desea incluir la funcionalidad de eliminación, le recomiendo que escriba la API para indicar explícitamente al iterador si debe eliminarse a la izquierda o a la derecha, por ejemplo, definiendo esos métodos como removeNext()
y removePrevious()
.