在Java

时间:2015-05-25 23:22:30

标签: java

我有一个基本的事件管理器设置,它遍历一个链接的监听器列表,以通知它们一个事件。监听器都通过层次结构(一个控制另一个)相关,并且在一个特定事件期间,对象将从列表中删除另一个。删除的对象位于层次结构的底部,没有自己的子项。

因为这个我当前正在收到ConcurrentModificationException,并且因为我想要删除的对象不是当前迭代的对象,所以我不能使用迭代器和iterator.remove()。

有什么干净的方法可以删除这个对象吗?我想避免将我的子对象重构为可重用,因为这会破坏我的测试套件,但我宁愿拥有高质量的代码。我在这个项目上使用Java 7,无法升级到Java 8。

示例代码如下,以及我想出的唯一想法。代码并不精确,只是为了显示我当前设置的结构。

活动经理通知:

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

听众示例:

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);
    }

    ...
}

我(粗略且未经考验)的想法:

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);
        }
    }
} 

虽然我的想法可能现在可以工作,但未来可能很容易破解。它也感觉不安全。

感谢您的帮助。

4 个答案:

答案 0 :(得分:2)

您可以在开始迭代它的位置复制侦听器集合以处理事件。例如:

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

你必须采取复制列表的方式,但这样for循环将在不同于你稍后修改的监听器集合上工作。

答案 1 :(得分:1)

即可。 Iterator.remove() Javadoc说(部分),

  

如果在迭代进行过程中以除了调用此方法之外的任何方式修改基础集合,则未指定迭代器的行为。

答案 2 :(得分:0)

您可以利用CopyOnWriteArrayList,检查以下代码。

    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();
        }
    }

答案 3 :(得分:0)

我查看了Frank Zheng提到的CopyOnWriteArrayList,看看这是不是我真正想要的。虽然不是,但它确实让我找到了ConcurrentLinkedQueue,它完美地处理了我的情况。