2011-08-18 92 views
3

我需要根據其ID來更新PriorityQueue中的某些固定優先級元素。我認爲這是一個相當常見的場景,這裏是一個示例代碼段(安卓2.2):迭代時更新PriorityQueue

for (Entry e : mEntries) { 
    if (e.getId().equals(someId)) { 
     e.setData(newData); 
    } 
} 

我再發條目「不可改變」(沒有setter方法),以便在創建新的Entry實例,並通過使用setData返回()。我修改我的方法到這一點:

for (Entry e : mEntries) { 
    if (e.getId().equals(someId)) { 
     Entry newEntry = e.setData(newData); 
     mEntries.remove(e); 
     mEntries.add(newEntry); 
    } 
} 

的代碼似乎很好地工作,但有人指出,修改隊列在遍歷這是一個壞主意:它可以拋出ConcurrentModificationException,我會需要添加我想要移除到ArrayList的元素並在稍後刪除它。他沒有解釋爲什麼,而且這對我來說看起來頗爲頭疼,但我在互聯網上找不到任何具體的解釋。

This post是相似的,但有優先級可以改變的,這不是我的情況)

誰能弄清楚什麼是錯我的代碼,我應該怎麼改變它, - 最重要的是 - 爲什麼?

感謝, Rippel


PS:有些實施細則...

PriorityQueue<Entry> mEntries = new PriorityQueue<Entry>(1, Entry.EntryComparator()); 

有:

public static class EntryComparator implements Comparator<Entry> { 
    public int compare(Entry my, Entry their) { 
     if (my.mPriority < their.mPriority) { 
      return 1; 
     } 
     else if (my.mPriority > their.mPriority) { 
      return -1; 
     } 
     return 0; 
    } 
} 
+0

也許創建一個堆棧,然後在完成後將想要的元素添加到那裏。 – Matt

回答

6

此代碼是在Java 6實現PriorityQueue中的批量添加:

private class Itr implements Iterator<E> { 
    /** 
    * The modCount value that the iterator believes that the backing 
    * Queue should have. If this expectation is violated, the iterator 
    * has detected concurrent modification. 
    */ 
    private int expectedModCount = modCount; 

    public E next() { 
    if(expectedModCount != modCount) { 
     throw new ConcurrentModificationException(); 
    } 


    } 

} 

現在,這是爲什麼代碼在這裏?如果您查看Javadoc for ConcurrentModificationException,您會發現如果在迭代完成之前對底層集合進行修改,則迭代器的行爲未定義。因此,許多收藏實施這種機制。

要解決你的代碼

你需要確保你不修改代碼中期循環。如果你的代碼是單線程的(就像它看起來那樣),那麼你可以簡單地按照你的同事的建議做,並將它複製到一個列表中供以後包含。此外,還記錄了使用Iterator.remove()方法以防止出現ConcurrentModificationException。舉個例子:

List<Entry> toAdd = new ArrayList<Entry>(); 
Iterator it = mEntries.iterator(); 
while(it.hasNext()) { 
    Entry e = it.next(); 

    if(e.getId().equals(someId)) { 
    Entry newEntry = e.setData(newData); 
    it.remove(); 
    toAdd.add(newEntry); 
    } 
} 
mEntries.addAll(toAdd); 
+0

很好的解釋和解決方案:) – berlindev

0

一個稍微好一點的實現是

List<Entry> toAdd = new ArrayList<Entry>(); 
for (Iterator<Entry> it= mEntries.iterator();it.hasNext();) { 
    Entry e = it.next(); 
    if (e.getId().equals(someId)) { 
     Entry newEntry = e.setData(newData); 
     it.remove(); 
     toAdd.add(newEntry); 
    } 
} 
mEntries.addAll(toAdd); 

這裏使用了迭代器的移除,之後

+0

@rippel啊是啊固定:) –

0

的PriorityQueue中的Javadoc說明確:

「請注意,此實現不是同步多線程不應同時訪問一個PriorityQueue例如,如果任何線程結構上修改的列表,而不是。 ,請使用線程安全的PriorityBlockingQueue類。「

這似乎是你的情況

0

什麼是錯在你的代碼進行了說明。 - 實現迭代器,它可以通過收集與相交的修改一貫迭代是相當艱鉅的任務,你需要指定如何處理刪除的項目(是否會通過迭代器看到?),添加的項目,修改的項目...即使您可以一致地執行,它也會相當複雜且效率不高 - 而且大多數情況下不是非常有用,因爲使用因此,Java架構師選擇在迭代時拒絕修改,並且來自Java集合API的大多數集合都會遵循這一點,並且如果檢測到這種修改,則會拋出ConcurrentModificationException。

至於你的代碼 - 對我來說,你不應該讓項目不可變。不變性是偉大的事情,但不應該過度使用。如果您在此處使用的Entry對象是某種域對象,並且您確實希望它們是不可變的 - 您可以創建某種臨時數據持有者(MutableEntry)對象,在算法中使用它,並將數據複製到Entry之前返回。從我的角度來看,這將是最好的解決方案。