2011-03-04 93 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實現在外面有:)

相關問題