javalinked-listruntime-error

How do I iterate over a linked list and add nodes to it at the same time?


I have an algorithm that can be implemented by iteratively modifying a finite integer indexed collection of lists of objects of a certain class X. At every iteration, every object in every list should be visited. At every visit, this object may need to be removed, and some number of other objects may need to be inserted, into the same list or into other lists. At some point it will be detected that an acceptable state has been reached and the iteration will stop, returning the whole collection of lists of X as the result.

In short, the algorithm — in its most straightforward reading — asks of me to do non-trivial deletions from and insertions into a linked list — and the linked list in question may or may not be being iterated over at the time.

I want to implement this algorithm in Java, and I thought I could use LinkedList<State>[] as my state type. I have eventually coerced my code into compiling, but it (not unexpectedly) throws the exception java.util.ConcurrentModificationException. I am not sure how to proceed. The LinkedList of Java seems to be less powerful than a true linked list — or else I wield it wrong.

Solutions I am considering

My question

Is there a better way?

* Am I missing some handy standard API that would let me do what I want with the LinkedList I have?

Simplified example

Here is a simplified example that I made by removing stuff from my actual code. If I can make it work, I should be able to make my actual code work as well. This code compiles but throws the exception java.util.ConcurrentModificationException at run time. The class X here is simply a box for int and the program will hopefully compute the hailstone sequence of the number 39, but what exactly it computes is not important — my actual task is entirely different.

import java.util.LinkedList;
import java.util.ListIterator;

class Example
{
    private LinkedList<Int> state_set;
    private int success_counter = 0;

    public Example (int number)
    {
        this.state_set = new LinkedList<Int> ( );
        this.state_set.add (new Int (number));
    }

    public void example ( )
    {
        while (true)
        {
            for (ListIterator<Int> iterator = this.state_set.listIterator ( ); iterator.hasNext ( );)
            {
                Int focussed_state = iterator.next ( );
                iterator.remove ( );
                if (focussed_state.value == 1)
                {
                    this.success_counter += 1;
                }
                else
                {
                    if (focussed_state.value % 2 == 0)
                    {
                        this.state_set.add (new Int (focussed_state.value / 2));
                    }
                    else
                    {
                        this.state_set.add (new Int (focussed_state.value * 2 + 1));
                    }
                }
            }

            if (this.success_counter >= 1)
                break;
        }
    }
}

class Int
{
    public int value;
    public Int (int value) { this.value = value; }
}

class Main
{
    public static void main (String [] args)
    {
        Example example = new Example (39);
        example.example ( );
        System.out.println ("Done!");
    }
}

Note that this program would succeed for the input 1 because in that case the linked list is never added to.


Solution

  • You seem to be using the linked list like a queue, so instead of iterating over it using an iterator, use the methods in Queue instead. poll elements from the linked list, stopping the loop if poll returns null, and offer new elements to the queue.

    Int focusedState;
    stateSet.listIterator(1).nextIndex()
    while ((focusedState = stateSet.poll()) != null)
    {
        if (focusedState.value == 1)
        {
            this.successCounter += 1;
            break;
        }
        else if (focusedState.value < 0) {
            break;
        }
        else if (focusedState.value % 2 == 0)
        {
            this.stateSet.offer(new Int(focusedState.value / 2));
        }
        else
        {
            this.stateSet.offer(new Int(focusedState.value * 2 + 1));
        }
    }
    

    If you are not purely using the linked list like a queue in your real code, you can just create a new Iterator after the adding an element, e.g.

    // remember where we are
    int currIndex = iterator.nextIndex();
    // add the element
    this.stateSet.add(...);
    // create a new iterator
    iterator = this.stateSet.listIterator(currIndex);
    

    Note that recreating the list iterator starting from currIndex will iterate from one end of the linked list to the specified index under the hood. If that is undesirable, I suggest using an ArrayList instead.

    If you don't need to add the element at the end of the list, you can also call ListIterator.add, which adds the new element just behind the cursor of the iterator (i.e. you can get it by calling previous()).


    There are other problems with the current code (seemingly useless outer while(true) loop, the linked list is only ever going to have at most one element, integer overflow, etc), but I assume this is because this is a simplified version of your real code.