Search code examples
javalistlinked-listlistiterator

Java LinkedList ListIterator behavior


I was working with a java.util.ListIterator on a java.util.LinkedList expecting it to work like in this pseudocode:

list = (1,2,3,4)
iterator.next should be 1
iterator.next should be 2
iterator.prev should be 1
iterator.next should be 2

But the order is like this:

iterator.next is 1
iterator.next is 2
iterator.prev is 2
iterator.next is 2

I couldn't believe that this is the way it works, so I created a test, but it produces the same output. So I had a closer look at the definition of ListIterator which of course is:

next()
Returns the next element in the list and advances the cursor position.
previous()
Returns the previous element in the list and moves the cursor position backwards.

So the implementation is correct, but I remain with the question why they have chosen this behavior? Wouldn't it be much more intuitiv the way i got it?

Here is the code of the test:

import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import java.util.LinkedList;
import java.util.ListIterator;

public class LinkedListTest {
    ListIterator<Integer> iterator;

    @Before
    public void setUp() throws Exception {
        LinkedList<Integer> list = new LinkedList<>();
        for (int i = 1; i < 5; i++) {
            list.add(i);
        }
        iterator = list.listIterator();
    }

    @Test
    public void successfullTest() throws Exception
    {
        assertEquals(1, (int) iterator.next());
        assertEquals(2, (int) iterator.next());
        assertEquals(2, (int) iterator.previous());
        assertEquals(2, (int) iterator.next());
        assertEquals(3, (int) iterator.next());
        assertEquals(4, (int) iterator.next());
    }

    @Test
    public void failingTest() throws Exception
    {
        assertEquals(1, (int) iterator.next());
        assertEquals(2, (int) iterator.next());
        assertEquals(1, (int) iterator.previous());
        assertEquals(2, (int) iterator.next());
        assertEquals(3, (int) iterator.next());
        assertEquals(4, (int) iterator.next());
    }
}

Solution

  • It is useful to imagine that iterators in Java never points to the specific element, but either before the first element, in the middle between two elements or just after the last element.

    So, when iterator created, it looks like

     1 2 3 4
    ^
    

    When you call next, 1 is returned and iterators moves forward:

     1 2 3 4
      ^    
    

    When you call next again, 2 is returned and iterators moves forward:

     1 2 3 4
        ^
    

    When you call prev, 2 is returned and iterators moves backward:

     1 2 3 4
      ^    
    

    So the next call to next will return 2.

    Notice that there is no way to get "current" value of the iterator. The only way to get the value is to move the iterator.

    The other way of implementing iterators we could see in C++. To use C++ iterator we need three separate actions: retrieve current value, check that there are move values to retrieve and move iterator. While java approach needs only two actions: check that there are move values to retrieve and get-value-and-move-iterator. So it is simpler to implement custom iterator in Java than in C++.