web-dev-qa-db-fra.com

HashSet vs ArrayList contient des performances

Lors du traitement de grandes quantités de données, je suis souvent amené à:

HashSet<String> set = new HashSet<String> ();
//Adding elements to the set
ArrayList<String> list = new ArrayList<String> (set);

Quelque chose comme "dumping" le contenu de l'ensemble dans la liste. Je fais généralement cela car les éléments que je rajoute contiennent souvent des doublons que je veux supprimer, ce qui semble être un moyen facile de les supprimer.

Avec seulement cet objectif en tête (éviter les doublons), je pourrais aussi écrire:

ArrayList<String> list = new ArrayList<String> ();
// Processing here
if (! list.contains(element)) list.add(element);
//More processing here

Et donc pas besoin de "dumping" l'ensemble dans la liste. Cependant, je ferais une petite vérification avant d’insérer chaque élément (ce que je suppose aussi que HashSet fait aussi) 

L'une des deux possibilités est-elle clairement plus efficace?

19
Jorge

L'ensemble donnera de bien meilleures performances (O(n) vs O(n^2) pour la liste), ce qui est normal car l'appartenance à un ensemble (l'opération contains) est l'objectif very d'un ensemble.

Contient pour une HashSet est O(1) par rapport à O(n) pour une liste. Par conséquent, vous ne devez jamais utiliser une liste si vous devez souvent exécuter contains.

48
Dici

ArrayList utilise un tableau pour stocker les données. Le ArrayList.contains sera de O(n) complexité. Donc, essentiellement, rechercher dans le tableau encore et encore aura la complexité O(n^2)

Alors que HashSet utilise un mécanisme de hachage pour stocker les éléments dans leurs compartiments respectifs. L'opération de HashSet sera plus rapide pour une longue liste de valeurs. Il atteindra l'élément dans O(1).

8
YoungHobbit

J'ai fait un test alors vérifiez le résultat:

Pour les éléments SAME STRING dans un HashSet, TreeSet, ArrayList et LinkedList, voici les résultats de

  1. 50.000 UUID
    • ARTICLE RECHERCHÉ: e608c7d5-c861-4603-9134-8c636a05a42b (index 25.000)
    • hashSet.contains (item)? VRAI 0 ms
    • treeSet.contains (item)? VRAI 0 ms
    • arrayList.contains (item)? VRAI 2 ms
    • linkedList.contains (item)? VRAI 3 ms
  2. 5.000.000 UUID
    • ARTICLE RECHERCHÉ: 61fb2592-3186-4256-a084-6c96f9322a86 (index 25.000)
    • hashSet.contains (item)? VRAI 0 ms
    • treeSet.contains (item)? VRAI 0 ms
    • arrayList.contains (item)? VRAI 1 ms
    • linkedList.contains (item)? VRAI 2 ms
  3. 5.000.000 UUID
    • ARTICLE RECHERCHÉ: db568900-c874-46ba-9b44-0e1916420120 (index 2.500.000)
    • hashSet.contains (item)? VRAI 0 ms
    • treeSet.contains (item)? VRAI 0 ms
    • arrayList.contains (item)? VRAI 33 ms
    • linkedList.contains (item)? VRAI 65 ms

Basé sur les résultats ci-dessus, il n'y a PAS une grosse différence entre l'utilisation de la liste de tableaux et celle d'un ensemble. Vous pouvez peut-être essayer de modifier ce code et remplacer le String par votre Object et voir les différences ensuite ...

    public static void main(String[] args) {
        Set<String> hashSet = new HashSet<>();
        Set<String> treeSet = new TreeSet<>();
        List<String> arrayList = new ArrayList<>();
        List<String> linkedList = new LinkedList<>();

        List<String> base = new ArrayList<>();

        for(int i = 0; i<5000000; i++){
            if(i%100000==0) System.out.print(".");
            base.add(UUID.randomUUID().toString());
        }

        System.out.println("\nBase size : " + base.size());
        String item = base.get(25000);
        System.out.println("SEARCHED ITEM : " + item);

        hashSet.addAll(base);
        treeSet.addAll(base);
        arrayList.addAll(base);
        linkedList.addAll(base);

        long ms = System.currentTimeMillis();
        System.out.println("hashSet.contains(item) ? " + (hashSet.contains(item)? "TRUE " : "FALSE") + (System.currentTimeMillis()-ms) + " ms");
        System.out.println("treeSet.contains(item) ? " + (treeSet.contains(item)? "TRUE " : "FALSE") + (System.currentTimeMillis()-ms) + " ms");
        System.out.println("arrayList.contains(item) ? " + (arrayList.contains(item)? "TRUE " : "FALSE") + (System.currentTimeMillis()-ms) + " ms");
        System.out.println("linkedList.contains(item) ? " + (linkedList.contains(item)? "TRUE " : "FALSE") + (System.currentTimeMillis()-ms) + " ms");
    }
5
urs86ro

Si vous n'avez pas besoin d'une liste, j'utiliserais simplement un ensemble. Il s'agit de la collection naturelle à utiliser si l'ordre n'a pas d'importance et que vous souhaitez ignorer les doublons.

Vous pouvez faire les deux si vous avez besoin d’une liste sans doublons. 

private Set<String> set = new HashSet<>();
private List<String> list = new ArrayList<>();


public void add(String str) {
    if (set.add(str))
        list.add(str);
}

De cette façon, la liste ne contiendra que des valeurs uniques, l'ordre d'insertion d'origine sera conservé et l'opération sera O (1).

4
Peter Lawrey

Vous pouvez ajouter des éléments à la liste elle-même ..__ Ensuite, pour déduire -

HashSet<String> hs = new HashSet<>(); // new hashset
hs.addAll(list); // add all list elements to hashset (this is the dedup, since addAll works as a union, thus removing all duplicates)
list.clear(); // clear the list
list.addAll(hs); // add all hashset elements to the list

Si vous avez juste besoin d'un ensemble avec déduction, vous pouvez également utiliser addAll () sur un autre ensemble, de sorte qu'il ne possède que des valeurs uniques.

0
Prateek Paranjpe