Come mantenere un elenco univoco in Java?


104

Come creare un elenco di oggetti univoci / distinti (senza duplicati) in Java?

In questo momento lo sto usando HashMap<String, Integer>per farlo poiché la chiave viene sovrascritta e quindi alla fine possiamo ottenere ciò HashMap.getKeySet()che sarebbe unico. Ma sono sicuro che dovrebbe esserci un modo migliore per farlo poiché la parte del valore viene sprecata qui.

Risposte:


164

Puoi utilizzare un'implementazione Set :

Alcune informazioni dal JAVADoc:

Una raccolta che non contiene elementi duplicati . Più formalmente, gli insiemi non contengono coppie di elementi e1 ed e2 tali che e1.equals (e2), e al massimo un elemento nullo. Come suggerisce il nome, questa interfaccia modella l'astrazione dell'insieme matematico.

Nota: è necessario prestare molta attenzione se gli oggetti modificabili vengono utilizzati come elementi dell'insieme. Il comportamento di un insieme non è specificato se il valore di un oggetto viene modificato in un modo che influisce sui confronti uguali mentre l'oggetto è un elemento dell'insieme. Un caso speciale di questo divieto è che non è consentito che un insieme contenga se stesso come elemento. »

Queste sono le implementazioni:

  • HashSet

    Questa classe offre prestazioni nel tempo costanti per le operazioni di base (aggiungi, rimuovi, contiene e ridimensiona), supponendo che la funzione hash disperda gli elementi correttamente tra i bucket. L'iterazione su questo set richiede un tempo proporzionale alla somma delle dimensioni dell'istanza HashSet (il numero di elementi) più la "capacità" dell'istanza HashMap di supporto (il numero di bucket). Pertanto, è molto importante non impostare la capacità iniziale troppo alta (o il fattore di carico troppo basso) se le prestazioni di iterazione sono importanti.

    Durante l'iterazione di a, HashSetl'ordine degli elementi ottenuti non è definito.

  • LinkedHashSet

    Implementazione di tabelle hash ed elenchi collegati dell'interfaccia Set, con ordine di iterazione prevedibile. Questa implementazione differisce da HashSet in quanto mantiene un elenco a doppio collegamento che attraversa tutte le sue voci. Questo elenco collegato definisce l'ordine di iterazione, che è l'ordine in cui gli elementi sono stati inseriti nel set (ordine di inserimento). Tieni presente che l'ordine di inserimento non viene influenzato se un elemento viene reinserito nel set. (Un elemento e viene reinserito in un insieme s se s.add (e) viene invocato quando s.contains (e) restituisce true immediatamente prima dell'invocazione.)

    Quindi, l'output del codice sopra ...

     Set<Integer> linkedHashSet = new LinkedHashSet<>();
     linkedHashSet.add(3);
     linkedHashSet.add(1);
     linkedHashSet.add(2);
    
     for (int i : linkedHashSet) {
         System.out.println(i);
     }

    ... sarà necessariamente

    3
    1
    2
  • TreeSet

    Questa implementazione fornisce un tempo di log (n) garantito per le operazioni di base (aggiungi, rimuovi e contiene). Per impostazione predefinita, gli elementi restituiti durante l'iterazione sono ordinati in base al loro " ordinamento naturale ", quindi il codice sopra ...

     Set<Integer> treeSet = new TreeSet<>();
     treeSet.add(3);
     treeSet.add(1);
     treeSet.add(2);
    
     for (int i : treeSet) {
         System.out.println(i);
     }

    ... produrrà questo:

    1
    2
    3

    (Puoi anche passare Comparatorun'istanza a un TreeSetcostruttore, facendogli ordinare gli elementi in un ordine diverso.)

    Si noti che l'ordine mantenuto da un set (indipendentemente dal fatto che venga fornito o meno un comparatore esplicito) deve essere coerente con uguale se si vuole implementare correttamente l'interfaccia Set. (Vedere Comparable o Comparator per una definizione precisa di coerente con uguale.) Ciò è così perché l'interfaccia Set è definita in termini di operazione uguale, ma un'istanza TreeSet esegue tutti i confronti di elementi utilizzando il suo metodo compareTo (o compare), quindi due gli elementi che sono considerati uguali con questo metodo sono, dal punto di vista dell'insieme, uguali. Il comportamento di un insieme è ben definito anche se il suo ordinamento non è coerente con uguale; semplicemente non obbedisce al contratto generale dell'interfaccia Set.


Ora sono confuso, quale dovrei usare? Ho solo bisogno di mantenere un elenco di stringhe uniche. Quindi, in pratica, anche quando viene aggiunta una stringa esistente, dovrebbe effettivamente essere aggiunta.

1
La scelta è tua ... HashSet è universale e veloce, il set di alberi è ordinato, LinkedHashset mantiene l'ordine di inserimento ...
Frank

6
Questa non è una LISTA ... quindi non tutti i metodi di interfaccia LISTA sono disponibili.
marcolopes

2
Un insieme non è un elenco, non posso cercare elementi per indice in un insieme in tempo O (1) (accesso casuale).
Wilmol

13

Voglio chiarire alcune cose qui per il poster originale a cui altri hanno accennato ma non hanno realmente dichiarato esplicitamente. Quando dici che vuoi un elenco univoco, questa è la definizione stessa di un insieme ordinato. Alcune altre differenze chiave tra l'interfaccia di impostazione e l'interfaccia di elenco sono che List consente di specificare l'indice di inserimento. Quindi, la domanda è: hai davvero bisogno dell'interfaccia elenco (ad es. Per compatibilità con una libreria di terze parti, ecc.), Oppure puoi riprogettare il tuo software per utilizzare l'interfaccia Set? Devi anche considerare cosa stai facendo con l'interfaccia. È importante trovare gli elementi in base al loro indice? Quanti elementi ti aspetti nel tuo set? Se hai molti elementi, è importante ordinare?

Se hai davvero bisogno di una lista che abbia solo un vincolo univoco, c'è la classe Apache Common Utils org.apache.commons.collections.list.SetUniqueList che ti fornirà l'interfaccia della lista e il vincolo univoco. Intendiamoci, questo interrompe l'interfaccia della lista però. Tuttavia, otterrai prestazioni migliori da questo se devi cercare nell'elenco per indice. Se riesci a gestire l'interfaccia Set e hai un set di dati più piccolo, LinkedHashSet potrebbe essere un buon modo per andare. Dipende solo dal design e dall'intento del tuo software.

Anche in questo caso, ci sono alcuni vantaggi e svantaggi in ogni raccolta. Alcuni inserimenti veloci ma letture lente, alcuni hanno letture veloci ma inserimenti lenti, ecc. Ha senso spendere una discreta quantità di tempo con la documentazione delle raccolte per apprendere completamente i dettagli più fini di ogni classe e interfaccia.


3
Questo non fornisce una risposta alla domanda. Per criticare o richiedere chiarimenti a un autore, lascia un commento sotto il suo post: puoi sempre commentare i tuoi post e una volta che hai una reputazione sufficiente potrai commentare qualsiasi post .
Zach Saucier

1
Fornisce una risposta, in realtà. Se vuole solo una lista che si comporti come un Set, usa org.apache.commons.collections.list.SetUniqueList, ma come programmatore, lui / noi dovremmo stare più attenti di questo e dovremmo pensare di più al problema. Se questo migliora la mia risposta, "Come creare un elenco univoco in Java?" List uniqueList = new SetUniqueList () ;, ecco come ....
Paul Connolly

3
E Zach, non sto cercando di essere un idiota, ma hai letto la mia risposta prima del tuo commento? O semplicemente non lo capisci? Se non lo capisci, va bene, fammelo sapere e approfondirò l'argomento. Non credo che dovrei scrivere un trattato sulle strutture dei dati per dare una risposta amichevole alla domanda di qualcuno. Né mi interessa fare un modo mansueto di costruire la mia reputazione di commento quando conosco la risposta e nessun altro l'ha davvero fornita.
Paul Connolly

1
E a proposito, non stavo né criticando né chiedendo chiarimenti all'autore, stavo solo dicendo che può A) usare rapidamente la classe che gli ho dato, o B) prendersi il tempo per capire veramente le differenze tra queste classi e relazionarsi loro alle sue esigenze. B ovviamente richiede più tempo, ma si tradurrà in un codice migliore a lungo termine.
Paul Connolly

8

Usa new HashSet<String> un esempio:

import java.util.HashSet;
import java.util.Set;

public class MainClass {
  public static void main(String args[]) {
    String[] name1 = { "Amy", "Jose", "Jeremy", "Alice", "Patrick" };

    String[] name2 = { "Alan", "Amy", "Jeremy", "Helen", "Alexi" };

    String[] name3 = { "Adel", "Aaron", "Amy", "James", "Alice" };

    Set<String> letter = new HashSet<String>();

    for (int i = 0; i < name1.length; i++)
      letter.add(name1[i]);

    for (int j = 0; j < name2.length; j++)
      letter.add(name2[j]);

    for (int k = 0; k < name3.length; k++)
      letter.add(name3[k]);

    System.out.println(letter.size() + " letters must be sent to: " + letter);

  }
}

2
Basta aggiungere il programma di cui sopra -> 11 lettere devono essere inviate a: [Aaron, Alice, James, Adel, Jose, Jeremy, Amy, Alan, Patrick, Helen, Alexi]
Ammad

4

Potresti semplicemente usare a HashSet<String>per mantenere una raccolta di oggetti unici. Se i Integervalori nella mappa sono importanti, puoi invece utilizzare il containsKeymetodo delle mappe per verificare se la tua chiave è già nella mappa.


3

HashSet<String>(o) qualsiasi Setimplementazione può fare il lavoro per te. Setnon consentire duplicati.

Ecco javadoc per HashSet.


2

Non so quanto sia efficiente, tuttavia ha funzionato per me in un contesto semplice.

List<int> uniqueNumbers = new ArrayList<>();

   public void AddNumberToList(int num)
    {
        if(!uniqueNumbers .contains(num)) {
            uniqueNumbers .add(num);
        }
    }

1

Potresti voler usare una delle classi di implementazione di java.util.Set<E>Interface, ad esempio java.util.HashSet<String> collection class.

Una raccolta che non contiene elementi duplicati. Più formalmente, gli insiemi non contengono coppie di elementi e1 ed e2 tali che e1.equals (e2), e al massimo un elemento nullo. Come suggerisce il nome, questa interfaccia modella l'astrazione dell'insieme matematico.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.