Esiste un'implementazione dell'elenco senza duplicati?


87

Lo so SortedSet, ma nel mio caso ho bisogno di qualcosa che implementi List, e non Set. Quindi esiste un'implementazione là fuori, nell'API o altrove?

Non dovrebbe essere difficile implementare me stesso, ma ho pensato perché non chiedere prima alle persone qui?


1
Perché è necessario implementare List? Gli insiemi sono iterabili, come le liste, quindi suppongo che il metodo di ricezione imponga List per qualche altro motivo.
Rapina il

@ Rob Esatto, è una richiesta esterna e la struttura dei dati include molto più di un elenco.
Yuval

Se l'utente desidera una LIST, è chiaro che necessita di metodi dell'interfaccia LIST che non sono presenti nell'interfaccia SET ...
marcolopes

Risposte:


94

Non esiste una raccolta Java nella libreria standard per farlo. LinkedHashSet<E>conserva l'ordinamento in modo simile a a List, tuttavia, se racchiudi il tuo set in a Listquando vuoi usarlo come a Listotterrai la semantica che desideri.

In alternativa, la Commons Collections (o commons-collections4, per la versione generica) ha un Listche fa già quello che vuoi: SetUniqueList/ SetUniqueList<E>.


6
La classe Commons è esattamente ciò di cui ho bisogno, ma il mio capo mi ha detto di implementarla prima o poi. 10 volte comunque!
Yuval

5
Ah beh, niente come reinventare la ruota! Adesso saprai se il bisogno si ripresenta, comunque. collections15 è una cosa abbastanza utile da avere in giro; Le MultiMaps in particolare alleviano il dolore di qualcosa che si finisce per implementarsi molto.
Calum

19
@skaffman: in realtà non è un idiota, ma a volte fa mosse che sono ... beh, strane. Ad ogni modo, non introdurrò bug nel prodotto. Nel mercato di oggi, sono contento del mio lavoro e non cerco di sbattere le porte e bruciare ponti, se ottieni il mio punto.
Yuval

3
Sono abbastanza sorpreso quando SetUniqueList non ha il tipo parametrizzato.
emeraldhieu

2
Jeffrey: Sulle piattaforme mobili il sistema di solito rimuove le classi inutilizzate, ma certo, ci sono molte ragioni per cui potresti non adottare una di queste soluzioni "normali". C'è sempre qualche compromesso da fare e nessuna soluzione risolverà tutti i casi.
Calum

14

Ecco cosa ho fatto e funziona.

Supponendo che avessi un ArrayListcon cui lavorare, la prima cosa che ho fatto è stata crearne uno nuovo LinkedHashMap.

LinkedHashSet<E> hashSet = new LinkedHashSet<E>()

Quindi provo ad aggiungere il mio nuovo elemento al file LinkedHashSet. Il metodo add non altera LinkedHasSete restituisce false se il nuovo elemento è un duplicato. Quindi questa diventa una condizione che posso testare prima di aggiungere al file ArrayList.

if (hashSet.add(E)) arrayList.add(E);

Questo è un modo semplice ed elegante per impedire l'aggiunta di duplicati a un elenco di array. Se vuoi puoi incapsularlo e sovrascrivere il metodo add in una classe che estende il ArrayList. Ricorda solo di occupartene addAllscorrendo gli elementi e chiamando il metodo add.


1
Sì, penso che questa sia la soluzione migliore, puoi anche usare semplicemente un normale HashSet, non un Linked, e poi puoi usare la tua lista come vuoi, puoi anche decidere cosa fare in alcune situazioni, come in aggiungendo un elemento all'interno di una lista prima di un indice specifico, si può decidere di spostare o meno l'elemento duplicato in questa posizione.
Gyurix

La migliore soluzione qui ... Pubblicherò il mio codice di classe
UniqueList

Questo ha funzionato per me, nel mio algoritmo BFS Graph. Perché avevo alcuni nodi che ho aggiunto a una coda (LinkedList) solo se non erano già presenti.
Jeancarlo Fontalvo

11

Quindi ecco cosa ho fatto alla fine. Spero che questo aiuti qualcun'altro.

class NoDuplicatesList<E> extends LinkedList<E> {
    @Override
    public boolean add(E e) {
        if (this.contains(e)) {
            return false;
        }
        else {
            return super.add(e);
        }
    }

    @Override
    public boolean addAll(Collection<? extends E> collection) {
        Collection<E> copy = new LinkedList<E>(collection);
        copy.removeAll(this);
        return super.addAll(copy);
    }

    @Override
    public boolean addAll(int index, Collection<? extends E> collection) {
        Collection<E> copy = new LinkedList<E>(collection);
        copy.removeAll(this);
        return super.addAll(index, copy);
    }

    @Override
    public void add(int index, E element) {
        if (this.contains(element)) {
            return;
        }
        else {
            super.add(index, element);
        }
    }
}   

10
Fai attenzione: LinkedList.contains () deve scansionare l'intero elenco per determinare se un oggetto è contenuto nell'elenco. Ciò significa che quando si aggiungono oggetti a un elenco di grandi dimensioni, l'intero elenco viene analizzato per ciascuna operazione di aggiunta (nel caso peggiore). Questo può finire per essere LENTO.
matt b

8
Inoltre, l'override di addAll non verifica la presenza di duplicati nella raccolta passati ad addAll ().
matt b

@mattb Come risolveresti questo problema: su Android, quando si associano oggetti a una visualizzazione di elementi di elenco, ci viene data la posizione dell'elemento nell'adattatore di visualizzazione. Poiché gli insiemi non hanno indice, l'unico modo è controllare se l'oggetto esiste o meno quando si usano gli elenchi è scorrere e cercare una copia esistente.
TheRealChx101

6

Perché non incapsulare un set con un elenco, ordinare come:

new ArrayList( new LinkedHashSet() )

Questo lascia l'altra implementazione per qualcuno che è un vero maestro delle collezioni ;-)


4
Questo costruttore copia il contenuto del Set nel nuovo List, invece di avvolgerlo.
Calum

@Calum, è corretto, ma invece di preoccuparsi di non aggiungere duplicati a un List, può aggiungere i suoi oggetti a un Set (e lasciare che il Set si preoccupi di filtrare i duplicati) e avvolgere semplicemente quel Set in un List quando lo passa a il metodo esterno.
matt b

4
Copia un set in un elenco ma non hai alcun ordine noto. Ma è di questo che si tratta.
Janning

4

Dovresti considerare seriamente la risposta di dhiller:

  1. Invece di preoccuparti di aggiungere i tuoi oggetti a un elenco senza duplicati, aggiungili a un Set (qualsiasi implementazione), che per sua natura filtrerà i duplicati.
  2. Quando è necessario chiamare il metodo che richiede un List, racchiuderlo in un new ArrayList(set)(o un new LinkedList(set), qualunque cosa).

Penso che la soluzione che hai pubblicato con il NoDuplicatesListabbia alcuni problemi, principalmente con il contains()metodo, inoltre la tua classe non gestisce il controllo dei duplicati nella raccolta passata al tuo addAll()metodo.


Mi piacerebbe conoscere questi problemi di contains (). Per quanto riguarda addAll (), creo una copia della raccolta data e rimuovo tutti gli oggetti già in 'this'. Come fa a non gestire i duplicati?
Yuval

Come ho accennato nel mio commento al tuo intervento di classe, contains () deve scansionare l'intero elenco (nel peggiore dei casi) per trovare se l'oggetto è contenuto nell'elenco. Se si dispone di un elenco di 1 milione di elementi e lo si aggiunge 10 singolarmente, (nel caso peggiore) vengono scansionati oltre dieci milioni di elementi.
matt b

Come per addAll (), se la Collection passata ad addAll contiene essa stessa dei duplicati, questi non vengono rilevati. Ad esempio: la tua lista {A, B, C, D} lista di parametri {B, D, E, E, E}. Si crea una copia del parametro e dopo removeAll contiene {E, E, E}.
matt b

Il problema addAll () non è realmente rilevante per me, poiché utilizzo NoDuplicatesList durante la procedura e addAll () dovrebbe ricevere un altro NoDuplicatesList come parametro. Cosa suggeriresti per migliorare le prestazioni di contains ()?
Yuval

3

Avevo bisogno di qualcosa del genere, quindi sono andato alle raccolte comuni e SetUniqueListho usato il , ma quando ho eseguito alcuni test delle prestazioni, ho scoperto che non sembra ottimizzato rispetto al caso se voglio usare a Sete ottenere un Arrayusando il Set.toArray()metodo.

Ci sono SetUniqueTestvoluti 20: 1 per riempire e quindi attraversare 100.000 stringhe rispetto all'altra implementazione, il che è una grande differenza.

Quindi, se ti preoccupi delle prestazioni, ti consiglio di usare Set and Get an Array invece di usare SetUniqueList, a meno che tu non abbia davvero bisogno della logica di SetUniqueList, allora dovrai controllare altre soluzioni ...

Metodo principale del codice di test :

public static void main(String[] args) {


SetUniqueList pq = SetUniqueList.decorate(new ArrayList());
Set s = new TreeSet();

long t1 = 0L;
long t2 = 0L;
String t;


t1 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
    pq.add("a" + Math.random());
}
while (!pq.isEmpty()) {
    t = (String) pq.remove(0);
}
t1 = System.nanoTime() - t1;

t2 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
    s.add("a" + Math.random());
}

s.clear();
String[] d = (String[]) s.toArray(new String[0]);
s.clear();
for (int i = 0; i < d.length; i++) {
    t = d[i];

}
t2 = System.nanoTime() - t2;

System.out.println((double)t1/1000/1000/1000); //seconds
System.out.println((double)t2/1000/1000/1000); //seconds
System.out.println(((double) t1) / t2);        //comparing results

}

Saluti, Mohammed Sleem


1

NOTA: non tiene conto dell'implementazione di subList .

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

public class UniqueList<T> extends ArrayList<T> {

    private static final long serialVersionUID = 1L;

    /** Unique elements SET */
    private final Set<T> set=new HashSet();

    /** Used by addAll methods */
    private Collection<T> addUnique(Collection<? extends T> col) {
        Collection<T> unique=new ArrayList();
        for(T e: col){
            if (set.add(e)) unique.add(e);
        }
        return unique;
    }

    @Override
    public boolean add(T e) {
        return set.add(e) ? super.add(e) : false;
    }

    @Override
    public boolean addAll(Collection<? extends T> col) {
        return super.addAll(addUnique(col));
    }

    @Override
    public void add(int index, T e) {
        if (set.add(e)) super.add(index, e);
    }

    @Override
    public boolean addAll(int index, Collection<? extends T> col) {
        return super.addAll(index, addUnique(col));
    }

}

0

La documentazione per le interfacce di raccolta dice:

Set: una raccolta che non può contenere elementi duplicati.
Elenco: una raccolta ordinata (a volte chiamata sequenza). Gli elenchi possono contenere elementi duplicati.

Quindi, se non vuoi duplicati, probabilmente non dovresti usare un elenco.


Ho menzionato specificamente che ho bisogno di un'implementazione di List. Credimi, c'è un motivo.
Yuval

Il motivo è perché stai interagendo con un'API che utilizza un elenco come parametro (invece di una raccolta)? È un po 'fastidioso avere a che fare con
matt b

In realtà l'API accetta una mappa <AccountType, Map <AccountType, List <Account> >>, il che significa che si tiene da qualche parte in prossimità di dozzine a centinaia di elenchi ... bah.
Yuval

La costruzione di funzioni di probabilità con coppie elemento-probabilità può comportare l'assenza di duplicati, sebbene gli elementi duplicati possano essere semplicemente uniti.
Al G Johnston

-1

nel addmetodo, perché non usareHashSet.add() per controllare i duplicati invece di HashSet.consist(). HashSet.add()tornerà truese nessun duplicato e in caso falsecontrario.


Che cos'è HashSet#consist()?
naXa

-1

Nella parte superiore della mia testa, gli elenchi consentono i duplicati. È possibile implementare rapidamente un UniqueArrayListe sovrascrivere tutte le funzioni add/ insertda controllare contains()prima di chiamare i metodi ereditati. Per uso personale, è possibile implementare solo il addmetodo che si utilizza e sovrascrivere gli altri per generare un'eccezione nel caso in cui i futuri programmatori cerchino di utilizzare l'elenco in un modo diverso.


Ero pronto a ricadere su questa idea (cosa che alla fine ho dovuto fare) se nessuno avesse suggerito qualcosa di meglio = Cool Vedi la mia risposta sopra.
Yuval

-3

Ho appena creato la mia UniqueList nella mia piccola libreria in questo modo:

package com.bprog.collections;//my own little set of useful utilities and classes

import java.util.HashSet;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Jonathan
*/
public class UniqueList {

private HashSet masterSet = new HashSet();
private ArrayList growableUniques;
private Object[] returnable;

public UniqueList() {
    growableUniques = new ArrayList();
}

public UniqueList(int size) {
    growableUniques = new ArrayList(size);
}

public void add(Object thing) {
    if (!masterSet.contains(thing)) {
        masterSet.add(thing);
        growableUniques.add(thing);
    }
}

/**
 * Casts to an ArrayList of unique values
 * @return 
 */
public List getList(){
    return growableUniques;
}

public Object get(int index) {
    return growableUniques.get(index);
}

public Object[] toObjectArray() {
    int size = growableUniques.size();
    returnable = new Object[size];
    for (int i = 0; i < size; i++) {
        returnable[i] = growableUniques.get(i);
    }
    return returnable;
    }
}

Ho una classe TestCollections simile a questa:

package com.bprog.collections;
import com.bprog.out.Out;
/**
*
* @author Jonathan
*/
public class TestCollections {
    public static void main(String[] args){
        UniqueList ul = new UniqueList();
        ul.add("Test");
        ul.add("Test");
        ul.add("Not a copy");
        ul.add("Test"); 
        //should only contain two things
        Object[] content = ul.toObjectArray();
        Out.pl("Array Content",content);
    }
}

Funziona bene. Tutto ciò che fa è aggiungere a un set se non lo ha già e c'è un Arraylist che è restituibile, così come un array di oggetti.


Sì, dovresti aggiungere un po 'più di metodi per implementare l'interfaccia List.
Gyurix
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.