¿Existe una implementación de lista no duplicada?

86

Lo sé SortedSet, pero en mi caso necesito algo que implemente List, y no Set. Entonces, ¿existe una implementación, en la API o en otro lugar?

No debería ser difícil de implementar yo mismo, pero pensé por qué no preguntar a la gente aquí primero

Yuval
fuente
1
¿Por qué necesita implementar List? Los conjuntos son iterables, como las listas, así que supongo que el método de recepción está imponiendo List por alguna otra razón.
Rob
@Rob Así es, es una demanda externa y la estructura de datos incluye muchísimas más de una Lista.
Yuval
Si el usuario quiere una LISTA, entonces está claro que necesita métodos de la interfaz LIST que no están presentes um la interfaz SET ...
marcolopes

Respuestas:

92

No hay una colección de Java en la biblioteca estándar para hacer esto. LinkedHashSet<E>Sin Listembargo, conserva el orden de manera similar a a , por lo que si envuelve su conjunto en a Listcuando quiera usarlo como a List, obtendrá la semántica que desea.

Alternativamente, las Colecciones Comunes (o commons-collections4, para la versión genérica) tiene un Listque ya hace lo que quieres: SetUniqueList/ SetUniqueList<E>.

Calum
fuente
5
La clase Commons es exactamente lo que necesito, pero mi jefe me dijo que eventualmente la implementara yo mismo. ¡10x de todos modos!
Yuval
5
¡Ah, bueno, nada como reinventar la rueda! De todos modos, sabrá ahora si surge la necesidad nuevamente. collections15 es algo bastante útil para jugar; MultiMaps, en particular, alivia el dolor de algo que uno termina implementando mucho.
Calum
19
@skaffman: en realidad no es un idiota, pero a veces hace movimientos que son ... bueno, extraños. De todos modos, no voy a introducir errores en el producto. En el mercado actual, estoy contento con mi trabajo y no quiero dar portazos y quemar puentes, si entiendes mi punto.
Yuval
3
Estoy bastante sorprendido cuando SetUniqueList no tiene un tipo parametrizado.
emeraldhieu
2
Jeffrey: En las plataformas móviles, el sistema generalmente eliminará las clases no utilizadas, pero claro, hay muchas razones por las que no puede optar por una de estas soluciones "normales". Siempre hay que hacer una compensación y ninguna solución solucionará todos los casos.
Calum
14

Esto es lo que hice y funciona.

Suponiendo que tengo un ArrayListarchivo con el que trabajar, lo primero que hice fue crear un archivo LinkedHashMap.

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

Luego intento agregar mi nuevo elemento al LinkedHashSet. El método add no altera el LinkedHasSety devuelve falso si el nuevo elemento es un duplicado. Entonces, esto se convierte en una condición que puedo probar antes de agregar al ArrayList.

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

Esta es una forma simple y elegante de evitar que se agreguen duplicados a una lista de matrices. Si lo desea, puede encapsularlo y anular el método add en una clase que amplíe el ArrayList. Solo recuerde lidiar con addAllrecorrer los elementos y llamar al método add.

usuario3570018
fuente
1
Sí, creo que esta es la mejor solución para ello, también puedes simplemente usar un HashSet normal, no un Linked, y luego puedes usar tu lista como quieras, también puedes decidir qué hacer en algunas situaciones, como en agregando un elemento dentro de una lista antes de un índice específico, puede decidir si desea mover el elemento duplicado a esta posición o no.
gyurix
La mejor solución aquí ... Publicaré mi código de clase
UniqueList
Esto funcionó para mí, en mi algoritmo BFS Graph. Porque tenía algunos nodos que agregué a una cola (LinkedList) solo si aún no estaban en.
Jeancarlo Fontalvo
11

Así que esto es lo que hice finalmente. Espero que esto ayude a alguien más.

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);
        }
    }
}   
Yuval
fuente
10
Tenga cuidado: LinkedList.contains () necesita escanear toda la lista para determinar si un objeto está contenido en la Lista. Esto significa que cuando agrega objetos a una Lista grande, se escanea toda la Lista para cada operación de adición (en el peor de los casos). Esto puede terminar siendo LENTO.
Matt b
8
Además, su anulación de addAll no comprueba si hay duplicados en la colección que se pasa a addAll ().
Matt b
@mattb ¿Cómo resolvería este problema entonces? En Android, cuando vinculamos objetos a una vista de elemento de lista, se nos da la posición del elemento en el adaptador de vista. Dado que los conjuntos no tienen índice, la única forma es verificar si el objeto existe o no cuando se usan listas es iterar y buscar una copia existente.
TheRealChx101
6

¿Por qué no encapsular un conjunto con una lista, ordenar como:

new ArrayList( new LinkedHashSet() )

Esto deja la otra implementación para alguien que sea un verdadero maestro de Colecciones ;-)

Daniel Hiller
fuente
4
Este constructor copia el contenido del Conjunto en la nueva Lista, en lugar de ajustarlo.
Calum
@Calum, eso es correcto, pero en lugar de preocuparse por no agregar duplicados a una Lista, puede agregar sus objetos a un Conjunto (y dejar que el Conjunto se preocupe por filtrar los duplicados) y simplemente envolver ese Conjunto en una Lista al pasarlo a el método externo.
Matt b
4
Esto copia un conjunto en una lista, pero no tiene ningún orden conocido. Pero de esto se trata la pregunta.
Enero
4

Debería considerar seriamente la respuesta de dhiller:

  1. En lugar de preocuparse por agregar sus objetos a una Lista sin duplicados, agréguelos a un Conjunto (cualquier implementación), que por naturaleza filtrará los duplicados.
  2. Cuando necesite llamar al método que requiere una Lista, envuélvalo en a new ArrayList(set)(o a new LinkedList(set), lo que sea).

Creo que la solución que publicaste con el NoDuplicatesListtiene algunos problemas, principalmente con el contains()método, además de que tu clase no controla la verificación de duplicados en la Colección pasada a tu addAll()método.

mate b
fuente
Me encantaría conocer estos problemas de contains (). En cuanto a addAll (), creo una copia de la colección dada y elimino todos los objetos que ya están en 'esto'. ¿Cómo no maneja eso los duplicados?
Yuval
Como mencioné en mi comentario a la publicación de su clase, contains () tiene que escanear la lista completa (en el peor de los casos) para encontrar si el objeto está contenido en la lista. Si tiene una lista de 1 millón de elementos y agrega 10 individualmente, entonces (en el peor de los casos) se escanean más de diez millones de elementos.
Matt b
En cuanto a addAll (), si la colección pasada a addAll contiene duplicados, no se detectan. Por ejemplo: su lista {A, B, C, D} lista de parámetros {B, D, E, E, E}. Creas una copia del parámetro, y después de removeAll it contains {E, E, E}.
Matt b
El problema de addAll () no es realmente relevante para mí, ya que uso NoDuplicatesList durante todo el procedimiento, y addAll () debería recibir otra NoDuplicatesList como parámetro. ¿Qué sugeriría para mejorar el rendimiento de contains ()?
Yuval
3

Necesitaba algo así, así que fui a las colecciones de commons y usé el SetUniqueList, pero cuando ejecuté una prueba de rendimiento, descubrí que no parece optimizado en comparación con el caso si quiero usar ay Setobtener un Arrayusando el Set.toArray()método.

La SetUniqueTesttomó 20: 1 vez para llenar y luego travesía 100.000 Cuerdas que comparan a la otra aplicación, que es una gran diferencia acuerdo.

Entonces, si te preocupa el rendimiento, te recomiendo que uses Set and Get an Array en lugar de usar el SetUniqueList, a menos que realmente necesites la lógica del SetUniqueList, entonces tendrás que verificar otras soluciones ...

Método principal del código de prueba :

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

}

Saludos, Mohammed Sleem

Grand Tour
fuente
1

NOTA: no tiene en cuenta la implementación de 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));
    }

}
marcolopes
fuente
0

La documentación para las interfaces de colección dice:

Conjunto: una colección que no puede contener elementos duplicados.
Lista: una colección ordenada (a veces llamada secuencia). Las listas pueden contener elementos duplicados.

Entonces, si no desea duplicados, probablemente no debería usar una lista.

Hauch
fuente
Mencioné específicamente que necesito una implementación de List. Créame, hay una razón.
Yuval
¿La razón es porque estás interactuando con una API que toma una lista como parámetro (en lugar de una colección)? Es un poco molesto tener que lidiar con eso
matt b
En realidad, la API toma un Map <AccountType, Map <AccountType, List <Account> >>, lo que significa mantener en algún lugar cerca de docenas o cientos de listas ... bah.
Yuval
La construcción de funciones de probabilidad con pares elemento-probabilidad puede implicar que no haya duplicados, aunque los elementos duplicados podrían simplemente fusionarse.
Al G Johnston
-1

en el addmétodo, ¿por qué no usar HashSet.add()para verificar duplicados en lugar de HashSet.consist(). HashSet.add()Volverá truesi no hay duplicado y de lo falsecontrario.

neoreeprast
fuente
¿Qué es HashSet#consist()?
naXa
-1

Fuera de mi cabeza, las listas permiten duplicados. Puede implementar rápidamente ay UniqueArrayListanular todas las funciones add/ insertpara verificar contains()antes de llamar a los métodos heredados. Para uso personal, solo puede implementar el addmétodo que usa y anular los demás para lanzar una excepción en caso de que los futuros programadores intenten usar la lista de una manera diferente.

Kieveli
fuente
Estaba listo para volver a esta idea (que finalmente tuve que hacerlo) si nadie sugirió algo mejor = 8-) Vea mi propia respuesta arriba.
Yuval
-3

Acabo de hacer mi propia UniqueList en mi propia pequeña biblioteca como esta:

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;
    }
}

Tengo una clase TestCollections que se ve así:

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);
    }
}

Funciona bien. Todo lo que hace es agregar a un conjunto si aún no lo tiene y hay una Arraylist que se puede devolver, así como una matriz de objetos.

Jonathan
fuente
Sí, debería agregarle un poco más de métodos para implementar la interfaz List.
gyurix