2011-03-04 78 views
3

我对某事有点困惑。 Java的文档告诉我们,当使用Iterator对象迭代该集合时,从集合中删除项目时没有定义的行为,唯一安全的方法是使用Iterator.remove()。在遍历事件处理程序集合时,如何安全地从*回调中的*删除处理程序?

怎么那么,你会安全地删除从一个ArrayList事件处理程序,如果在通过列表迭代的过程中,处理程序的一个决定,是时候去掉自己作为一个监听?

// in public class Dispatcher 

public void dispatchEvent(){ 
    Iterator<IEventHandler> iterator = mHandlers.iterator(); 
    IEventHandler handler = null; 
    while(iterator.hasNext()){ 
     handler = iterator.next(); 
     handler.onCallbackEvent(); 
    } 
} 

public void insertHandler(IEventHandler h){ 
    mHandlers.add(h); 
} 

public void removeHandler(IEventHandler h){ 
    mHandlers.remove(h); 
} 

同时,处理程序被实例化这样的...

final Dispatcher d = new Dispatcher(); 
d.insertHandler(new IEventHandler(){ 
    @Override 
    public void onCallbackEvent(){ 
     Log.i(" callback happened "); 
     d.removeHandler(this); 
    } 
}); 

看到潜在的问题?您删除从ArrayList中的处理程序为onCallbackEvent()的结果,在这特别的处理声明,而你还在使用迭代器迭代。

这是一个棘手的问题?处理这种情况的安全方法是什么?

回答

3

实现事件系统时,这是一个非常普遍的问题。唯一的解决方案是复制更改处理程序的列表。你可以在insertHandler/removeHandler方法中自己完成,或者使用CopyOnWriteArrayList。

+0

谢谢你的建议。这确实不幸。我不得不想知道迭代器如果没有提供一种安全的方法来修改底层集合,那么迭代器有什么意义。 **注意:是的,我知道Iterator的设计模式。不过,似乎这种安全性是迭代器应该向你购买的东西,如果你麻烦去使用它的话。 – scriptocalypse 2011-03-04 23:38:09

+0

为了让您在寻找迭代安全性,迭代器必须复制或执行同样计算密集的任务。你能想出另外一种方法来实现一个安全的迭代器吗?您必须复制更改或复制迭代。在大多数情况下,复制更改比较合适,因为更改频率低于迭代次数。默认集合不提供迭代安全性,因为并非所有情况都需要它。这就是为什么有ArrayList和CopyOnWriteArrayList。 – 2011-03-04 23:44:05

+0

性能足够了。感谢您的洞察力! – scriptocalypse 2011-03-05 01:25:20

2

你可以重新实现removeHandler存储计划删除的处理程序。

public void removeHandler(IEventHandler h){ 
    mHandlersToRemove.add(h); 
} 

然后在您做任何调度之前删除它们。

public void dispatchEvent(){ 
    mHandlers.removeAll(mHandlersToRemove); 
    mHandlersToRemove.clear(); 
    ... 

您也可以删除在dispatchEvent年底,但你可以从处理程序只能去除。 (否则你可能会分派给中删除的处理程序。)


如果你有兴趣的理论解决了这个问题,你可以看看C++如何实现迭代器。在stl向量中,迭代器有一个erase method,它返回下一个有效的迭代器。

这将是这个样子:

for (itr = listA.begin(); itr != listA.end();) 
{ 
    if (shouldRemove(*itr)) { 
     itr = listA.erase(itr); 
    } 
    else { 
     ++itr; 
    } 
} 

当然,因为它无论是在C这个例子并不适用于你的问题++,这将是尴尬传播新的迭代器直到顶级别循环(或为您的呼叫添加一个返回值,以实现“删除”条件)。但也许有一个类似的Java实现在外面有:)