2010-02-26 57 views
14

這是一個有效的方式找到並使用每個循環在Java中從一個LinkedList刪除項目,是有可能,不一致可能出現:鏈表:刪除對象

for(ObjectType ob : obList) { 
    if(ob.getId() == id) { 
    obList.remove(ob); 
    break; 
    } 
} 

回答

16

其他人所說的正確的觀點,通常這不是你如何remove從對象採集。但是,在這種情況下,一旦你remove一旦出現break就沒有問題。

但是,如果您想在remove之後繼續迭代,則需要使用迭代器。否則,你會得到一個ConcurrentModificationException,或者在更一般的情況下,未定義的行爲。

所以,是的,如果你removebreakforeach後,你會沒事的


對於那些誰在說,這將失敗,因爲你不能在foreach修改的集合 - 這是真實的,只有當你想保留迭代。這不是這種情況,所以這個捷徑很好。

A ConcurrentModificationException被迭代器檢查並拋出。在這裏,remove(其中有資格作爲併發修改)後,你break出循環。迭代器甚至沒有機會檢測到它。

,如果你在break添加評論,爲什麼它是絕對必要的,等等,因爲如果這個代碼後來被修改以繼續remove迭代後,它會失敗這可能是最好的。

我會像對待這個成語類似goto(或者更確切地說,標記break/continue):它可能在最初看起來錯的,但是用在刀刃上的時候,它使一個更乾淨的代碼。

+0

我還沒有檢查過語言規範,但不是foreach語法,只是在後臺使用迭代器的編譯器魔法? – Powerlord 2010-02-26 17:56:37

+0

是的,它確實在場景後面使用迭代器,並且該迭代器對您是隱藏的。 – polygenelubricants 2010-02-26 17:58:27

+3

但是,當nexte開發人員嘗試添加一些功能而不「看到」該陷阱時,它很可能在未來的版本中失敗。那麼你應該記錄清楚。 – whiskeysierra 2010-02-26 17:58:57

4

編輯:的確,它會不要因爲休息而失敗。細節見polygenelubricant的答案。

但是,這是很危險的做法。爲了在Java中同時迭代和修改集合,必須使用「ListIterator」對象,並使用迭代器自己的「add()」和「remove()」方法,而不要使用集合中的那些方法。

您可以檢查Java文檔的「java.util.Iterator中」和「java.util.ListIterator中的」類

+0

「Iterator」沒有「add()」方法。只有ListIterator有。你應該指出。 – whiskeysierra 2010-02-26 17:56:43

+1

@Zorglub,注意'break'。這段代碼不會失敗。 – polygenelubricants 2010-02-26 18:03:19

+3

它可能不會「失敗」,但代碼在任何正常意義上都是錯誤的。 – 2010-02-26 18:05:13

1

嘗試這樣:

Iterator<ObjectType> iter = obList.iterator(); 
while (iter.hasNext()) { 
    ObjectType ob = iter.next(); 
    if(ob.getId() == id) { 
    iter.remove(); 
    break; 
    } 
} 

這就是一個迭代器不能用foreach循環替換的最後的地方之一。

+0

如果您在「移除」後出現「中斷」,則無需將其設置爲冗長。 – polygenelubricants 2010-02-26 17:54:35

+0

@polygenelubricants:哦,你說得對。我沒有意識到異常只會發生在下一個循環中。 – Stroboskop 2010-02-26 17:59:15

+2

如果這是一個鏈表,那麼Iterator.remove()比List.remove(Object)更有效,因爲後者必須重新搜索對象。我會使用迭代器,原則上它會被刪除。 – 2010-02-26 18:52:24

0

A CopyOnWriteArrayList可能是你在找什麼。當執行可變操作時,會創建一個底層數組的副本。這允許在for-each循環內修改列表元素。請記住,這不是一個鏈表,而且可能效率很低。

import java.util.List; 
import java.util.concurrent.CopyOnWriteArrayList; 

public class Main { 

    public static void main(String[] args) { 
     List<String> myList = new CopyOnWriteArrayList<String>(); 

     myList.add("a"); 
     myList.add("b"); 
     myList.add("c"); 

     // Will print [a, b, c] 
     System.out.println(myList); 

     for (String element : myList) { 
      if (element.equals("a")) { 
       myList.remove(element); 
      } 
     } 

     // Will print [b, c] 
     System.out.println(myList); 
    } 

} 
6

你應該使用iterator.remove()

從底層集合 由 迭代器(可選操作)返回的最後一個元素刪除。這個 方法只能調用一次,每 調用一次。一個 迭代器的行爲是不確定如果的 底層的集合被修改 在迭代過程中在 不是通過調用此方法 以外的任何方式。

+0

「迭代正在進行中」 - 這是這裏的重要區別。在他的情況下,一旦他修改了這個集合,他就放棄了這個迭代。這就是爲什麼我們要進行這個討論。但對於一般情況,你絕對正確。 – polygenelubricants 2010-02-26 18:22:07

1

爲了避免ConcurrentModifiationException ,你可以這樣做:

final Iterator<ObjectType> i = obList.iterator(); 
while (i.hasNext()) { 
    if (i.next().getId() == id) { 
     i.remove(); 
    } 
} 

for (int i = 0; i < obList.size(); i++) { 
    if (obList[i].getId() == id) { 
     obList.remove(i); 
    } 
} 

我寧願第一。處理索引更容易出錯,迭代器可以高效地實現。第一個建議與Iterable一起使用,而第二個建議需要List。

+0

只需在for循環中使用迭代器即可。 – 2010-02-26 18:15:45

+0

這會使循環的最後部分爲空。我不喜歡那樣。一陣子完全適合那裏。 – whiskeysierra 2010-02-26 18:32:55

+0

此代碼可能會執行多次刪除。這是與原始代碼不同的行爲,最多刪除一個元素。 – polygenelubricants 2010-02-26 18:42:24

7

最好使用迭代器,並通過遍歷集合來搜索對象以移除它時使用它的remove方法。這是因爲

  1. 收集可能是,例如,一個鏈表(和你的情況是),其remove方法意味着搜索對象一遍,哪些搜索可能有O(n)的複雜性。
  2. 除非您使用迭代器的remove方法,否則您不能在刪除後繼續迭代。現在您正在刪除第一個事件 - 將來您可能需要刪除所有匹配事件,在這種情況下,您必須重新編寫循環。

我建議,原則上,前述的增強並使用這樣的事情,而不是:

for(Iterator<ObjectType> it=obList.iterator(); it.hasNext();) { 
    if(it.next().getId()==id) { 
     it.remove(); 
     break; 
     } 
    } 

這樣,你不做出關於可能在將來改變基礎列表假設。


比較的代碼,以消除迭代器刪除所謂的最後一個條目(格式化太陽):

private E remove(Entry<E> e) { 
    if (e == header) 
     throw new NoSuchElementException(); 

    E result = e.element; 
    e.previous.next = e.next; 
    e.next.previous = e.previous; 
    e.next = e.previous = null; 
    e.element = null; 
    size--; 
    modCount++; 
    return result; 
} 

反對什麼刪除(對象)必須做到:

public boolean remove(Object o) { 
    if (o==null) { 
     for (Entry<E> e = header.next; e != header; e = e.next) { 
      if (e.element==null) { 
       remove(e); 
       return true; 
      } 
     } 
    } else { 
     for (Entry<E> e = header.next; e != header; e = e.next) { 
      if (o.equals(e.element)) { 
       remove(e); 
       return true; 
      } 
     } 
    } 
    return false; 
} 
+0

你沒有回答我所問的內容,但這很好。 – Xolve 2010-02-27 15:17:23

0

以上第二個循環應該改變一下

for (int i = 0; i < obList.size();) { 
    if (obList.get(i).getId() == id) { 
     obList.remove(i); 
     continue 
    } 
    ++i; 
} 

for (int i = obList.size() - 1; i >= 0; --i) { 
    if (obList.get(i).getId() == id) { 
     obList.remove(i); 
    } 
}