允许在循环期间修改列表

时间:2019-04-21 01:20:58

标签: java list concurrency

在大多数情况下,用户直接在原始循环内修改数组。在我的情况下,数组是通过在循环的控制之外的循环内部调用的方法修改的。我需要一种让局外人能够响应事件添加或删除值的方法。

我目前正在研究事件系统,例如输入。事件系统大部分都在运行,但是有时响应某个事件,侦听器实际上会希望停止侦听事件(例如,如果用户单击鼠标按钮离开一个菜单,则该菜单的侦听器将从输入侦听器中删除自己)。

但是,与此相关的问题是,当侦听器在事件侦听器方法中将自身删除时,它会修改正在循环的数组以调用所述事件方法。结果为ConcurrentModificationException。我想出的一种解决方案是克隆列表,这样它就不会在循环过程中被修改。但是,我认为这可能会占用大量资源,并且会大大降低性能。

我想出的另一个功能系统是在遍历侦听器时有两个列表(一个用于添加和删除)。如果在循环过程中调用了add或remove方法,则这些列表将添加这些值。在循环结束时,将相应地添加和删除值。这样做的问题是需要状态管理,并且看起来很凌乱。是否有比我刚才描述的两件事更好的解决此问题的方法?

/**
 * The base listener.
 */
public interface Listener {

    /**
     * Called when an event occurs.
     *
     * @param id
     *      the ID of the event.
     */
    public void event(int id);

}

/**
 * The test implementation of the base listener.
 */
public class TestListener implements Listener {

    @Override
    public void event(int id) {
        if(id == 0xDEADBEEF) {
            /*
             * This is where the error occurs.
             */
            TestSystem.removeListener(this);
        }
    }

}

/*
 * The revolutionary system that does foo and bar!
 */
class TestSystem {

    private static final ArrayList<Listener> LISTENERS = new ArrayList<Listener>();

    /**
     * Adds a listener.
     *
     * @param listener
     *      the listener to add.
     */
    private static void addListener(Listener listener) {
        LISTENERS.add(listener);
    }

    /**
     * Removes a listener.
     *
     * @param listener
     *      the listener to remove.
     */
    private static void removeListener(Listener listener) {
        LISTENERS.remove(listener);
    }

    /**
     * Calls an event.
     *
     * @param event
     *      the event to call.
     */
    private static void callEvent(Consumer<? super Listener> event) {
        for(Listener listener : LISTENERS) {
            event.accept(listener);
        }
    }

    public static void main(String[] args) {
        addListener(new TestListener()); // Add listener
        callEvent(listener -> listener.event(0xDEADBEEF)); // Call event
    }

}

如果您需要更多信息,请在评论中让我知道。从此代码生成的堆栈跟踪为:

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
    at java.util.ArrayList$Itr.next(ArrayList.java:859)
    at org.ardenus.engine.list.ConcurrentLoop.callEvent(ConcurrentLoop.java:72)
    at org.ardenus.engine.list.ConcurrentLoop.main(ConcurrentLoop.java:81)

0 个答案:

没有答案