2
votes

Quelle collection Java permet la suppression d'élément la plus rapide?

Je recherche une collection qui permette la suppression d'élément la plus rapide. J'ai testé ArrayList sur 1 million de lignes et il s'avère que la suppression du premier élément est plus rapide que la suppression du dernier. Il faut environ 50 secondes pour supprimer un million d'éléments

import java.util.ArrayList;

public class TestArray {

    final int numberOfElements = 1000000; 

    public void testArray () { 

    // test array 
    ArrayList<String> testArray = new ArrayList<String>(); 
    for (int i = 0; i < numberOfElements; i++) { 
        testArray.add("" + Math.random()); 
    }

    // testing speed when removing the first element  
    long startTime = System.currentTimeMillis(); 
    while (true) { 
        if (testArray.isEmpty()) { 
            System.out.println("Milliseconds to fisnish when removing the first element " + (System.currentTimeMillis() - startTime));
            break; 
        } 
        else { 
            String testString = testArray.get(0); 
            testArray.remove(testString); 
        }
    } 

    testArray = new ArrayList<String>(); 
    for (int i = 0; i < numberOfElements; i++) { 
        testArray.add("" + Math.random()); 
    } 
    // testing speed when removing the last   element  
        long startTime2 = System.currentTimeMillis(); 
        while (true) { 
            if (testArray.isEmpty()) { 
                System.out.println("Milliseconds to fisnish when removing the last element " + (System.currentTimeMillis() - startTime2));
                break; 
            } 
            else { 
                String testString = testArray.get(testArray.size()-1); 
                testArray.remove(testString); 
            }
        }



    } 

}

Mais je ne suis pas sûr que ce soit le moyen le plus rapide possible. 50 secondes est-il le moyen le plus rapide? Ou y a-t-il une meilleure collection, par exemple LinkedList le fera-t-il plus rapidement? Ou quelle est la collection la plus rapide pour supprimer des éléments un par un?


9 commentaires

Si vous supprimez par valeur (plutôt que par index), utilisez un Set. Sinon, vous le forcez à parcourir toute la collection pour trouver votre valeur.


Habituellement, une structure de données n'implique pas que des suppressions - vous devez également considérer les autres opérations (dont vous avez besoin)


L'index fonctionnera pour moi aussi. Je cherche le moyen le plus rapide


Votre référence est imparfaite, vous mesurez les effets secondaires, pas le code réel. Lisez comment faire des microbenchmarks corrects.


Et cela est prouvé par le fait que vous avez le boîtier le plus rapide exactement de l'arrière vers l'avant. La suppression du dernier élément d'une «ArrayList» est considérablement plus rapide que la suppression du premier.


@ user207421 Mais le trouver ne l'est pas.


@shmosel l'index du dernier élément est toujours size () - 1 , pas besoin de boucler si vous voulez toujours le dernier élément


@Ferrybig Qui a dit que nous voulions le dernier élément?


@Ferrybig mais la méthode remove (Object) ne sait pas que l'OP passe le dernier élément.


3 Réponses :


3
votes

1) Vous devriez envisager la LinkedList qui a des performances O (1) Big O pour l'opération supprimer (Explication ci-dessous) < / strong>, alors que ArrayList est O (n).
2) Vous pouvez essayer HashSet si vous n'êtes pas intéressé par les doublons.

LinkedList Supprimer:

1) La suppression de LinkedList au début et à la fin est en temps constant car la traversée n'est pas nécessaire.

2) La suppression des éléments du milieu prend plus de temps car l'élément doit être trouvé en premier.

3) Si vous avez un itérateur à l'emplacement que vous souhaitez supprimer, alors remove est en temps constant.


7 commentaires

La suppression de LinkedList est uniquement O (1) au début et à la fin. L'enlèvement au milieu prend beaucoup plus de temps.


@ user2357112 Dépend, si vous supprimez via un handle de nœud, son temps toujours constant (par exemple via ListIterator)


@ user2357112 Si vous le parcourez déjà pour trouver une valeur, la suppression sera moins chère pour LinkedList.


@Zabuza Ou via remove (Object) , comme dans la question.


@shmosel: remove (Object) n'est pas un temps constant. La suppression via un itérateur déjà positionné sur l'élément que vous souhaitez supprimer est un temps constant, mais cela dépend d'un modèle d'utilisation spécifique dont la réponse ne fait aucune mention.


@ user2357112 Je n'ai pas dit que c'était du temps constant. Je dis que la partie linéaire de cette méthode, à savoir l'itération, est nécessaire, qu'il s'agisse d'ArrayList ou LinkedList, mais la suppression ultérieure se fera en temps constant pour LinkedList uniquement.


ArrayDeque prend également en charge la suppression rapide au début et à la fin, sans la surcharge de LinkedList . Et lorsque la suppression via l'index est correcte, ArrayList peut toujours être le choix préféré.



2
votes

La meilleure collection pour les performances est TreeSet parce que si vous insérez des objets selon Comparable / Comparator, la collection sera ordonnée.

Mes temps: Liste des tableaux Millisecondes pour terminer lors de la suppression du premier élément 698 Millisecondes pour terminer lors de la suppression du dernier élément 121960

TreeSet: Millisecondes pour terminer lors de la suppression du premier élément 55 Millisecondes pour terminer lors de la suppression du dernier élément 50

AVERTISSEMENT: Avec ces solutions, vous ne pouvez pas avoir d'objets en double dans la collection.

@Test
    public void testTreeSet() {
        /* RESULTS
         * Milliseconds to fisnish when removing the first element 55
         * Milliseconds to fisnish when removing the last element 50
         */

        // test array
        TreeSet<String> testArray = new TreeSet<String>();
        int numberOfElements = 100000;
        for (int i = 0; i < numberOfElements; i++) {
            testArray.add("" + Math.random());
        }

        // testing speed when removing the first element
        long startTime = System.currentTimeMillis();
        while (true) {
            if (testArray.isEmpty()) {
                System.out.println("Milliseconds to fisnish when removing the first element "
                        + (System.currentTimeMillis() - startTime));
                break;
            } else {
                //String testString = testArray.get(0);
                String testString = testArray.first();
                testArray.remove(testString);
            }
        }

        testArray = new TreeSet<String>();
        for (int i = 0; i < numberOfElements; i++) {
            testArray.add("" + Math.random());
        }
        // testing speed when removing the last element
        long startTime2 = System.currentTimeMillis();
        while (true) {
            if (testArray.isEmpty()) {
                System.out.println("Milliseconds to fisnish when removing the last element "
                        + (System.currentTimeMillis() - startTime2));
                break;
            } else {
                //String testString = testArray.get(testArray.size() - 1);
                String testString = testArray.last();
                testArray.remove(testString);
            }
        }

    }


0 commentaires

2
votes

Premièrement: il doit y avoir un problème avec votre benchmark, une ArrayList supprime les éléments beaucoup plus lentement que d'en ajouter. Cela est dû au fait que le tableau ne doit pas avoir d'espaces dans le tableau sous-jacent. Les éléments doivent donc être déplacés si vous supprimez partout mais à la fin.

Cette réponse dépend si vous voulez supprimer les index ou les valeurs. En général, les opérations basées sur des index sont plus rapides car aucune comparaison de valeur expansive ne doit être effectuée. Étant donné que si vous souhaitez supprimer des éléments, vous devez les avoir ajoutés une fois, il est également utile de prendre en compte la complexité ajoutée

  • ArrayList: Ajoutez: O (n), amorti O (1) (en pratique bon marché). Remove est toujours O (n), Find O (1) si basé sur un index, O (n) si basé sur une valeur

Exemple pratique de l'effet de l'analyse amortie: l'ajout d'un million d'éléments d'affilée se traduira par 10 millions d'exemplaires. Cependant le nombre de copies est O (log n), n est le nombre d'opérations d'ajout consécutives.

  • LinkedList Ajouter: O (n) à la moyenne, AddFirst / Last O (1), removeLast / First O (1), find O (n), getFirstElement / GetLastElement O (1). Notez ici: Vous devez savoir que l'élément que vous recherchez est à la fin / au début et appeler la méthode correspondante.

Jusqu'à présent, si vous avez beaucoup d'opérations d'ajout / suppression consécutives et peu d'opérations de recherche (sauf pour obtenir le premier ou le dernier élément), je vous recommande d'utiliser LinkedList .

Si vous n'avez pas deux objets identiques, c'est-à-dire que ( Object.equals (sameObject) ) renvoie true pour exactement le même objet. vous devez utiliser LinkedHashSet Il a O (1) pour toutes les opérations, mais les objets égaux ne peuvent être contenus qu'une seule fois. Malheureusement, la recherche par index n'est pas possible ici, les méthodes ne sont pas non plus snychronisées. Mais il y a toujours un compromis.

Une théorie: selon les articles mentionnés ici nous ne pouvons pas faire mieux qu'Omega amorti (log n) pour l'ajout et la suppression arbitraires d'éléments.


4 commentaires

ArrayList est un tableau dynamique.


La page wiki ne mentionne rien sur les valeurs nulles. Autoriser des espaces dans le tableau sous-jacent ferait des recherches indexées O (n).


@shmosel Vous avez raison. Je savais que nous pouvions contourner les vilains changements lorsque nous utilisons une structure arborescente équilibrée supplémentaire, donc j'ai pensé que dans ce cas particulier, il y avait de bonnes chances d'obtenir O (1) . Apparemment non, je mettrai à jour ma réponse.


Votre «exemple dans la pratique» n'a aucun sens. D'où vient ce «10 millions d'exemplaires» et que voulez-vous dire par «nombre de copies» que vous prétendez être «O (log n)» alors que «O (…)» n'est pas du tout un nombre?