在大多数情况下,用户直接在原始循环内修改数组。在我的情况下,数组是通过在循环的控制之外的循环内部调用的方法修改的。我需要一种让局外人能够响应事件添加或删除值的方法。
我目前正在研究事件系统,例如输入。事件系统大部分都在运行,但是有时响应某个事件,侦听器实际上会希望停止侦听事件(例如,如果用户单击鼠标按钮离开一个菜单,则该菜单的侦听器将从输入侦听器中删除自己)。
但是,与此相关的问题是,当侦听器在事件侦听器方法中将自身删除时,它会修改正在循环的数组以调用所述事件方法。结果为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)