简体   繁体   中英

Remove List Item without Iterator.remove() in Java

I have a basic event manager setup that iterates through a linked list of listeners to notify them of an event. The listeners are all related through a hierarchy (one controls another) and during one particular event, an object will remove another from the list. The removed object is at the bottom of the hierarchy and has no children of its own.

I'm currently getting ConcurrentModificationException because of this, and because the object I'm trying to remove is not the object of the current iteration, I cannot use an iterator and iterator.remove().

Is there any clean way I can remove this object? I would like to avoid a restructuring of my child object to be reusable because this would break a lot of my test suite, but I would rather have quality code. I am using Java 7 on this project and cannot upgrade to Java 8.

Example code is below, as well as the only idea that I've come up with. The code is not exact, but merely meant to show the structure of my current set up.

Event Manager Notification:

public void notifyListeners(Object sender, EventArgs args) {
    for (Listener l : listeners) {
        l.notify(sender, args);
    }
}

Listener Example:

public class Listener {

    ...

    private Listener myChild;

    public void notify(Object sender, EventArgs args) {
        if (myChild != null) {
            eventManager.removeListener(myChild);
        }

        mychild = new Child();
        eventManager.addListener(myChild);
    }

    ...
}

My (rough and untested) idea:

public void notifyListeners(Object sender, EventArgs args) {
    int listSize = listeners.size();

    for (int i = 0; i < listSize; i++) {
        l.notify(sender, args);

        if (listeners.size() != listSize) {
            listSize = listeners.size();
            i = listeners.indexOf(l);
        }
    }
} 

While my idea may work for now, this could easily break in the future. And it also just doesn't feel safe.

Thanks for the help.

You can copy the collection of listeners at the point that you begin iterating through it to process the event. For instance:

for(Listener l : new ArrayList<Listener>(listeners)) {

You'll have to take the hit of copying the list, but this way the for loop will be working over a different collection of listeners than the one you're later modifying.

No . The Iterator.remove() Javadoc says (in part),

The behavior of an iterator is unspecified if the underlying collection is modified while the iteration is in progress in any way other than by calling this method.

You can take advantage of CopyOnWriteArrayList, check the following code.

    import java.util.List;
    import java.util.ArrayList;
    import java.util.concurrent.CopyOnWriteArrayList;
    public  class  Test{

       List<String> stringList = new CopyOnWriteArrayList<String>();

       public void test(){
           for(int i=0;i<100;i++){
               stringList.add("item"+i);
           }

           new IteratorThread().start();
           new RemoveThenAddThread().start();
       }
       class IteratorThread extends Thread{
           public void run(){
               while(true) {
                   for (String l : stringList) {
                       System.out.println(l);
                       //DO SOMETHING
                       try {
                           Thread.sleep(10);
                       } catch (InterruptedException e) {
                           e.printStackTrace();
                       }
                   }
               }
           }
       }

        class RemoveThenAddThread extends Thread{
            public void run(){

                while(true) {
                    stringList.remove(0);
                    stringList.add("string item");

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }


        public static void main(String[] args) 
        {
            new Test().test();
        }
    }

I looked into CopyOnWriteArrayList mentioned by Frank Zheng to see if that was what I really wanted. While it wasn't, it did lead me to find the ConcurrentLinkedQueue, which handles my situation perfectly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM