Esiste un'implementazione dell'elenco non duplicata là fuori?
-
06-07-2019 - |
Domanda
Conosco SortedSet
, ma nel mio caso ho bisogno di qualcosa che implementa List
e non Set
. Quindi esiste un'implementazione là fuori, nell'API o altrove?
Non dovrebbe essere difficile implementarmi, ma ho pensato perché non chiedere prima alle persone qui?
Soluzione
Non ci sono raccolte Java nella libreria standard per farlo. LinkedHashSet<E>
conserva l'ordinamento in modo simile a un < =>, tuttavia, quindi se avvolgi il tuo set in List
quando vuoi usarlo come commons-collections4
otterrai la semantica che desideri.
In alternativa, le Collezioni comuni (o SetUniqueList
, per la versione generica ) ha un SetUniqueList<E>
che fa già quello che vuoi: <=> / <=> .
Altri suggerimenti
Ecco cosa ho fatto e funziona.
Supponendo che ho un ArrayList
per lavorare con la prima cosa che ho fatto è stato creato un nuovo LinkedHashMap
.
LinkedHashSet<E> hashSet = new LinkedHashSet<E>()
Quindi provo ad aggiungere il mio nuovo elemento a LinkedHashSet
. Il metodo add non modifica LinkedHasSet
e restituisce false se il nuovo elemento è un duplicato. Quindi questa diventa una condizione che posso verificare prima di aggiungere a addAll
.
if (hashSet.add(E)) arrayList.add(E);
Questo è un modo semplice ed elegante per impedire che i duplicati vengano aggiunti a un elenco di array. Se lo desideri, puoi incapsularlo e sovrascrivere il metodo add in una classe che estende <=>. Ricorda solo di gestire <=> eseguendo il ciclo tra gli elementi e chiamando il metodo add.
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);
}
}
}
Dovresti considerare seriamente la risposta di Dhiller:
- Invece di preoccuparti di aggiungere i tuoi oggetti a un elenco senza duplicati, aggiungili a un set (qualsiasi implementazione), che filtrerà per natura i duplicati.
- Quando è necessario chiamare il metodo che richiede un Elenco, racchiuderlo in un
new ArrayList(set)
(o unnew LinkedList(set)
, qualunque cosa).
Penso che la soluzione che hai pubblicato con NoDuplicatesList
abbia alcuni problemi, principalmente con il metodo contains()
, inoltre la tua classe non gestisce il controllo dei duplicati nella Raccolta passati al tuo metodo addAll()
.
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 ;-)
Avevo bisogno di qualcosa del genere, quindi sono andato alle raccolte comuni e ho usato SetUniqueList, ma quando ho eseguito alcuni test delle prestazioni, ho scoperto che non sembra ottimizzato rispetto al caso se voglio usare un set e ottenere un Array usando il metodo Set.toArray (), SetUniqueTest ha impiegato 20: 1 tempo per riempire e poi attraversare 100.000 stringhe rispetto alle altre implementaion, il che è una grande differenza, quindi se ti preoccupi delle prestazioni, ti consiglio di usare Set e ottieni un array invece di usare SetUniqueList, a meno che tu non abbia davvero bisogno della logica di SetUniqueList, quindi devi 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 http://abusleem.net/blog
NOTA: non tiene conto dell'implementazione 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));
}
}
La documentazione per le interfacce di raccolta dice:
Imposta & # 8212; una raccolta che non può contenere elementi duplicati.
Elenco & # 8212; una raccolta ordinata (a volte chiamata sequenza). Gli elenchi possono contenere elementi duplicati.
Quindi, se non vuoi duplicati, probabilmente non dovresti usare un elenco.
nel metodo add
, perché non usare HashSet.add()
per controllare i duplicati anziché HashSet.consist()
.
true
restituirà false
se nessun duplicato e <=> altrimenti.
In cima alla mia testa, gli elenchi consentono duplicati. È possibile implementare rapidamente un UniqueArrayList
e sovrascrivere tutte le funzioni add
/ insert
per verificare la presenza di contains()
prima di chiamare i metodi ereditati. Per uso personale, puoi implementare solo il metodo <=> che usi e sovrascrivere gli altri per generare un'eccezione nel caso in cui i futuri programmatori provino a utilizzare l'elenco in un modo diverso.
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 che assomiglia 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.