Search code examples
javaarraylistiteratorinsertion-sort

Java listIterator - swap 2 values


I have an ordered ArrayList of strings: I wrote this static method to keep it sorted, i use it every time an element is is added to the ArrayList.

Using listIterator, I start from the last value of ArrayList N: if N-1 comes after N I invert the two values. Then I go to cell n-1, and repeat the same procedure, until i reach the end of ArrayList.

In this code I am unable to invert current with prev: what am I doing wrong?

public static void sort(ArrayList arr, String s){

    if(arr.size()>1){

        ListIterator<String> iterator = arr.listIterator(arr.size());
    
        while(iterator.hasPrevious()){
            String current = iterator.previous();
            String prev = iterator.previous();    
            if(compare(current,prev) < 0){
                
                iterator.set(current);
                iterator.next();
                iterator.set(prev);
                iterator.previous();

            } else {
                break;
            }
        }
    }
}

Solution

  • From documentation of ListIterator#set(E e) we can read

    Replaces the last element returned by next() or previous() with the specified element

    So lets see what happens in your swapping code for data like arr=["b", "c", "a"].

    I will mark position of iterator with < or > to point out direction of potential set method.

    So it will be

    • [elementE] < elementF if it was called after next() which returned elementE
    • elementD > [elementE] if it was called after previous() which returned elementE

    Lets take a look at code you are using to swap elements.

    iterator.set(current); //1
    iterator.next();       //2
    iterator.set(prev);    //3
    iterator.previous();   //4
    

    Before its execution, state of your application (iterator and variables) is

    //current = "a"; prev="c", arr=["b", "c", "a"]
    "b" > "c" "a"  
         |
       position of iterator
    

    So since iterator points to element "c" it would be replaced if we call set now.

    After calling iterator.set(current); where current="a" state of application will change to

    //current = "a"; prev="c", arr=["b", "a", "a"]
    "b" > "a" "a"  //`iterator.set` doesn't affect "position" of iterator
    

    Now after iterator.next(); it will become

    //current = "a"; prev="c", arr=["b", "a", "a"]
    "b" "a" < "a"  //notce iterator was moved between "a" and "a" 
                   //but points at *left* "a"
    

    And here is your problem.

    Take a look at direction of iterator (which is <) and again read documentation quoted at start of this answer. Currently iterator.set(prev); will replace "a" in the middle, not at the end because it was middle element which was returned by either next() or previous() as last element.

    If you want to replace last "a" you need iterator to "point at" that element. So you want to create situation like

    1. "b" "a" > "a" - points from left
    2. or "b" "a" "a" < - points from right

    (also remember to set iterator after swapping in position like "b" "a" < "a" or "b" "a" > "a" so it would be "before" two next elements which we want to compare).

    In case 1 your swapping code can look like

    //"b" > "c" "a"
    iterator.set(current); //"b" > current "a"
    iterator.next();       //"b" current < "a"
    iterator.next();       //"b" current "a" <
    iterator.previous();   //"b" current > "a" 
    iterator.set(prev);    //"b" current > prev 
    

    (I will leave code for case 2 as your homework)


    BTW you need to handle case where new element is smallest in entire list (like in scenario in current answer with ["b", "c", "a"] (you will need nested iterator.hasPrevious())