2009-08-21 194 views
32

如果他們符合條件,我需要從ArrayList中刪除一些對象,並且我想知道哪種方式更有效。從Java中的ArrayList中刪除對象

這裏的情況:我有一個類包含一個ArrayList包含一些其他對象。我必須遍歷這個ArrayList並刪除滿足特定條件的所有元素。 據我所知,這些將是我的選擇刪除:

  1. 創建一個新的ArrayList並添加不符合條件的元素。迭代之後,從舊的ArrayList切換到沒有元素的新ArrayList。

  2. 創建一個新的ArrayList並添加滿足條件的元素。迭代後,使用removeAll()方法將ArrayList傳遞給要刪除的對象。

是否有更有效的方法從ArrayList刪除對象?

+2

除非您確定性能在代碼中的這個特定點上是個問題,否則我會建議忽略效率。還有一些其他的事情你應該考慮,例如:你是否保留引用原始列表的其他地方的變化應該體現?那麼你不能使用1.你可以使用'ArrayList.remove()',我。即「equals()」的語義是否像列表中的對象一樣工作? – 2009-08-21 08:02:47

+0

那麼,我談論的對象包含一些數組列表,我必須對它們做同樣的處理。我不知道這可能是一個瓶頸(我還沒有測試過),但我想知道你們是如何刪除項目,看看我是否有更好的選擇。回答你的第二個問題:是的,我可以使用remove()方法。 – 2009-08-21 08:09:25

回答

16

另一種方式:Iterator有一個可選的remove() - 方法,它是爲ArrayList實現的。迭代時可以使用它。

雖然我不知道,哪個變種是最高性能的,你應該測量它。如果在中間添加或刪除了一個元素,ArrayList必須複製所有的元素,這是真的(對於removeAll()也是如此)。對於這種情況,LinkedList應該更好。但是,由於我們都不知道自己的真實使用情況,所以最好的辦法就是測量所有變體,以選擇最佳解決方案。

+0

儘管有一個'remove()'的警告:它可能不會刪除您當前正在查看的對象。從JavaDoc中引用:更正式地,刪除索引最小的元素,使得(o == null?get(i)== null:o.equals(get(i)))(如果存在這樣的元素)。 「因此,取決於列表中的對象,它可能無法按預期工作。基本上,如果您可以在不損害應用程序的情況下將ArrayList交換爲SortedSet,那麼您應該可以。 – 2009-08-21 07:55:43

+3

這就是我在javadoc中看到的Iterator的remove()方法:「從底層集合中移除迭代器返回的最後一個元素(可選操作)。這種方法只能在下一次調用時調用一次。迭代器的行爲是未指定的,如果基礎集合在迭代過程中以除了調用此方法以外的任何方式進行修改。「我錯過了什麼? – Buhb 2009-08-21 08:04:01

+0

感謝您的答案,但我認爲Mnementh應該得到正確的答案因爲他是第一個回答的人 – 2009-08-21 08:36:48

0

也許Iteratorremove()方法? JDK的默認集合類應該支持所有支持此方法的創建者迭代器。

47

您可以在遍歷ArrayList時向後迭代和刪除。這具有隨後的元件不需要移位並且比向前移動更容易編程的優點。

+0

不錯的一個,+1 :)不知道這個性能改進是否真的很明顯。 – 2009-08-21 07:58:01

+0

在某些情況下是的 - 如果您擔心的話,可能是需要分析的其中一項。通常我會採取建立另一個項目清單的策略 - 其他程序員更容易理解。 – RichardOD 2009-08-21 07:59:54

+0

謝謝Richard,這是一個我從未想到的有趣方法! +1 – 2009-08-21 08:11:43

4

首先,我確定這確實是一個性能瓶頸,否則我會選擇最乾淨,最具表現力的解決方案。

如果是性能瓶頸,只要嘗試不同的策略,看看最快的是什麼。我的選擇是創建一個新的ArrayList並將所需的對象放在那個對象中,丟棄舊的ArrayList。

5

顯然,你提到數字1的兩種方法中效率更高,因爲它只需要遍歷列表一次,而方法數字2必須遍歷列表兩次(首先找到要刪除的元素,並將它們刪除)。

實際上,從另一個列表中刪除元素列表可能是一種比O(n)更差的算法,所以方法2更糟糕。

iterator方法:

List data = ...; 

for (Iterator i = data.iterator(); i.hasNext();) { 
    Object element = i.next(); 

    if (!(...)) { 
     i.remove(); 
    } 
} 
+1

就像你使用Iterator的方式一樣! – manix 2013-03-02 23:54:42

12

最高效的會,我想,是使用listIterator方法,做一個反向迭代:

for (ListIterator<E> iter = list.listIterator(list.size()); iter.hasPrevious();){ 
    if (weWantToDelete(iter.previous())) iter.remove(); 
} 

編輯:很久以後,我們也可以想要使用lambda或方法引用從列表(或任何集合!)添加the Java 8 way of removing elements。在就地filter爲集合,如果你喜歡:

list.removeIf(e -> e.isBad() && e.shouldGoAway()); 

這可能是清理收集的最佳方式。由於它使用內部迭代,因此集合實現可以採用快捷方式使其儘可能快(對於ArrayList,它可以最大限度地減少所需的複製量)。

1

除非你是積極的,你面臨的問題確實是一個瓶頸,我會去爲可讀

public ArrayList filterThings() { 

    ArrayList pileOfThings; 
    ArrayList filteredPileOfThings = new ArrayList(); 

    for (Thing thingy : pileOfThings) { 
     if (thingy.property != 1) { 
      filteredPileOfThings.add(thingy); 
     }    
    } 
    return filteredPileOfThings; 
} 
-2

我擅長與Mnementh的recommentation。
只是一個警告,雖然,

ConcurrentModificationException 

記住,你不要有一個以上的線程運行。如果執行多個線程並且線程不能很好地同步,則可能會出現此異常。

+1

不完全 - 如果底層集合在迭代時發生變化,將從快速收集集合中拋出。例如你迭代lst,其中一個調用是直接調用lst上的remove,而不是使用ListIterator上的remove方法。 – pjp 2009-08-21 10:11:26

1

從ArrayList中刪除元素存在隱藏成本。每次刪除元素時,都需要移動元素以填充「洞」。平均而言,這將需要N/2分配N個元素的列表。

因此,從N元素ArrayList中移除M個元素的平均值爲O(M * N)。 O(N)解決方案涉及創建新列表。例如。

List data = ...; 
List newData = new ArrayList(data.size()); 

for (Iterator i = data.iterator(); i.hasNext();) { 
    Object element = i.next(); 

    if ((...)) { 
     newData.add(element); 
    } 
} 

如果N大,我的猜測是,這種方法會比remove方法對小3或4

M的值快,但創造newList足夠大是很重要的保留list中的所有元素以避免在展開後複製後備數組。

0

我已經找到一個替代更快的解決方案:

int j = 0; 
    for (Iterator i = list.listIterator(); i.hasNext();) { 
    j++; 

    if (campo.getNome().equals(key)) { 
     i.remove(); 
     i = list.listIterator(j); 
    } 
    } 
1
int sizepuede= listaoptionVO.size(); 
for (int i = 0; i < sizepuede; i++) { 
    if(listaoptionVO.get(i).getDescripcionRuc()==null){ 
     listaoptionVO.remove(listaoptionVO.get(i)); 
     i--; 
     sizepuede--; 
    } 
} 

編輯:增加縮進

0

隨着迭代器,你可以隨時處理這些涉及訂購元素,而不是指定的索引。所以,你不應該對上述問題感到困擾。

Iterator itr = list.iterator(); 
String strElement = ""; 
while(itr.hasNext()){ 

    strElement = (String)itr.next(); 
    if(strElement.equals("2")) 
    { 
    itr.remove(); 
    } 
0

雖然這是違反直覺的,但這是我加速這項操作的數量巨大的方式。

正是我正在做:

ArrayList < HashMap < String , String >> results; // This has been filled with a whole bunch of results 

的ArrayList < HashMap中<字符串,字符串>>丟棄= findResultsToDiscard(結果);

results.removeall(discard);

但是,刪除所有方法需要6秒以上(不包括獲得丟棄結果的方法)從2000(ish)數組中移除約800個結果。

我嘗試了gustafc和其他人在這篇文章中提出的迭代器方法。

這確實加快了操作(下降到約4秒),但是這還不夠好。所以,我想的東西冒險...

ArrayList < HashMap < String, String>> results; 

    List <Integer> noIndex = getTheDiscardedIndexs(results); 

for (int j = noIndex.size()-1; j >= 0; j--){ 
    results.remove(noIndex.get(j).intValue()); 
} 

而getTheDiscardedIndexs節約指數的而不是包含HashMap數組的數組。事實證明,加速刪除對象的速度更快(現在大約爲0.1秒),並且會更有效率,因爲我們不需要創建大量要刪除的結果。

希望這可以幫助別人。