web-dev-qa-db-fra.com

Suppression d'objets d'une liste de tableaux dans Java

J'ai besoin de supprimer certains objets d'un ArrayList s'ils remplissent une condition et je me demande quel moyen pourrait être plus efficace.

Voici la situation: j'ai une classe qui contient un ArrayList contenant d'autres objets. Je dois parcourir cette ArrayList et supprimer tous les éléments remplissant une certaine condition. Pour autant que je sache, ce seraient mes options pour supprimer:

  1. Créez un nouveau ArrayList et ajoutez les éléments qui ne remplissent pas la condition. Après l'itération, passez de l'ancien arraylist au nouveau sans les éléments.

  2. Créez un nouveau ArrayList et ajoutez les éléments qui remplissent la condition. Après l'itération, utilisez la méthode removeAll() en passant le ArrayList avec les objets à supprimer.

Existe-t-il un moyen plus efficace de supprimer des objets d'un ArrayList?

32
Carlos Pastor

Une autre façon: l'itérateur a une méthode remove () facultative, qui est implémentée pour ArrayList. Vous pouvez l'utiliser pendant l'itération.

Je ne sais pas cependant, quelle variante est la plus performante, vous devez la mesurer.

starblue a commenté, que la complexité n'est pas bonne, et c'est vrai (pour removeAll () aussi), car ArrayList doit copier tous les éléments, si au milieu est un élément ajouté ou supprimé. Pour ces cas, une LinkedList devrait mieux fonctionner. Mais, comme nous ne connaissons pas tous vos cas d'utilisation réels, le mieux est de trop mesurer toutes les variantes pour choisir la meilleure solution.

16
Mnementh

Vous pouvez parcourir en arrière et supprimer au fur et à mesure que vous parcourez la liste de tableaux. Cela présente l'avantage que les éléments suivants n'ont pas besoin de se déplacer et est plus facile à programmer que d'avancer.

47
RichardOD

Je pense que le plus performant utiliserait la méthode listIterator et ferait une itération inverse:

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

Edit: Beaucoup plus tard, on pourrait aussi vouloir ajouter la méthode Java 8 pour supprimer les éléments à partir d'une liste (ou de n'importe quelle collection!) en utilisant une référence lambda ou une méthode. Une place filter pour les collections, si vous le souhaitez:

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

C'est probablement la meilleure façon de nettoyer une collection. Puisqu'il utilise l'itération interne, l'implémentation de la collection pourrait prendre des raccourcis pour la rendre aussi rapide que possible (pour ArrayLists, elle pourrait minimiser la quantité de copie nécessaire).

12
gustafc

De toute évidence, des deux méthodes que vous mentionnez le numéro 1 est plus efficace, car il n'a besoin de parcourir la liste qu'une seule fois, tandis qu'avec la méthode numéro 2, la liste doit être parcourue deux fois (d'abord pour trouver les éléments à supprimer, puis les retirez-les).

En fait, la suppression d'une liste d'éléments d'une autre liste est probablement un algorithme pire que O(n) donc la méthode 2 est encore pire.

La méthode itérateur:

List data = ...;

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

    if (!(...)) {
        i.remove();
    }
}
5
Jesper

Tout d'abord, je m'assurerais qu'il s'agit vraiment d'un goulot d'étranglement en matière de performances, sinon j'opterais pour la solution la plus propre et la plus expressive.

Si c'est IS un goulot d'étranglement des performances, essayez simplement les différentes stratégies et voyez ce qui est le plus rapide. Mon pari est de créer une nouvelle ArrayList et de placer les objets souhaités dans celle-ci, en rejetant l'ancienne ArrayList.

4
Buhb
int sizepuede= listaoptionVO.size();
for (int i = 0; i < sizepuede; i++) {
    if(listaoptionVO.get(i).getDescripcionRuc()==null){
        listaoptionVO.remove(listaoptionVO.get(i));
        i--;
        sizepuede--;
     }
}

modifier: indentation ajoutée

1
Raulitoxd

Il y a un coût caché à supprimer des éléments d'une ArrayList. Chaque fois que vous supprimez un élément, vous devez déplacer les éléments pour remplir le "trou". En moyenne, cela prendra N / 2 Affectations pour une liste avec N éléments.

Donc, supprimer M éléments d'un élément N ArrayList est O(M * N) en moyenne. Une solution O(N) implique la création d'une nouvelle liste. Par exemple.

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

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

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

Si N est grand, je suppose que cette approche sera plus rapide que l'approche remove pour des valeurs de M aussi petites que 3 ou 4.

Mais il est important de créer newList suffisamment grand pour contenir tous les éléments dans list pour éviter de copier le tableau de sauvegarde lorsqu'il est développé.

1
Stephen C

À moins que vous ne soyez certain que le problème auquel vous êtes confronté est en effet un goulot d'étranglement, j'irais pour le

public ArrayList filterThings() {

    ArrayList pileOfThings;
    ArrayList filteredPileOfThings = new ArrayList();

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

J'ai trouvé une solution alternative plus rapide:

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

    if (campo.getNome().equals(key)) {
       i.remove();
       i = list.listIterator(j);
    }
  }
0
Matteo Fattore

Peut-être Iterator ’s remove () method? Les classes de collection par défaut du JDK doivent tous les itérateurs de créateurs qui prennent en charge cette méthode.

0
Bombe

Bien que cela soit contre-intuitif, c'est la façon dont j'ai accéléré cette opération énormément.

Exactement ce que je faisais:

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

ArrayList <HashMap <String, String>> discard = findResultsToDiscard (résultats);

results.removeall (jeter);

Cependant, la méthode de suppression totale prenait plus de 6 secondes (sans inclure la méthode pour obtenir les résultats de suppression) pour supprimer environ 800 résultats d'un tableau de 2000 (ish).

J'ai essayé la méthode itérateur suggérée par Gustafc et d'autres sur ce post.

Cela a accéléré légèrement l'opération (jusqu'à environ 4 secondes), mais ce n'était toujours pas suffisant. J'ai donc essayé quelque chose de risqué ...

 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());
}

tandis que les getTheDiscardedIndexs enregistrent un tableau d'index plutôt qu'un tableau de HashMaps. Cela s'avère accélérer la suppression des objets beaucoup plus rapidement (environ 0,1 seconde maintenant) et sera plus efficace en mémoire car nous n'avons pas besoin de créer un large éventail de résultats pour les supprimer.

J'espère que cela aide quelqu'un.

0
Aiden Fry

Avec un itérateur, vous pouvez toujours gérer l'élément qui vient à l'ordre, pas un index spécifié. Donc, vous ne devriez pas être dérangé par la question ci-dessus.

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

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